We are using java.util.ResourceBundle to load property information. Our property file has grown so huge and we are thinking of splitting the master property file into several sub modules. Is it possible to achieve this?
master.properties
==>
master.properties
include moduleA.properties
include moduleB.properties
Let me know?
First of all, I wonder why you've chosen java.util.ResourceBundle over java.util.Properties. Given how your question is formulated, you don't seem to care about localization/internationalization nor about bundle file inheritance.
With Properties it's extraordinary easy since it implements Map which in turn offers a putAll() method to merge another map. Kickoff example:
Properties master = new Properties();
master.load(masterInput);
Properties moduleA = new Properties();
moduleA.load(moduleAinput);
master.putAll(moduleA);
Properties moduleB = new Properties();
moduleB.load(moduleBinput);
master.putAll(moduleB);
// Now `master` contains the properties of all files.
If you really insist in using ResourceBundle, your best bet is to create a custom ResourceBundle wherein you contol the loading by a custom Control.
Assuming that you've the following entry in master.properties which represents a commaseparated string with base names of the module properties files:
include=moduleA,moduleB
Then the following custom ResourceBundle example should work:
public class MultiResourceBundle extends ResourceBundle {
protected static final Control CONTROL = new MultiResourceBundleControl();
private Properties properties;
public MultiResourceBundle(String baseName) {
setParent(ResourceBundle.getBundle(baseName, CONTROL));
}
protected MultiResourceBundle(Properties properties) {
this.properties = properties;
}
#Override
protected Object handleGetObject(String key) {
return properties != null ? properties.get(key) : parent.getObject(key);
}
#Override
#SuppressWarnings("unchecked")
public Enumeration<String> getKeys() {
return properties != null ? (Enumeration<String>) properties.propertyNames() : parent.getKeys();
}
protected static class MultiResourceBundleControl extends Control {
#Override
public ResourceBundle newBundle(
String baseName, Locale locale, String format, ClassLoader loader, boolean reload)
throws IllegalAccessException, InstantiationException, IOException
{
Properties properties = load(baseName, loader);
String include = properties.getProperty("include");
if (include != null) {
for (String includeBaseName : include.split("\\s*,\\s*")) {
properties.putAll(load(includeBaseName, loader));
}
}
return new MultiResourceBundle(properties);
}
private Properties load(String baseName, ClassLoader loader) throws IOException {
Properties properties = new Properties();
properties.load(loader.getResourceAsStream(baseName + ".properties"));
return properties;
}
}
}
(trivial exception handling and localization handling is left aside, this is up to you)
This can be used as:
ResourceBundle bundle = new MultiResourceBundle("master");
You can programatically, however, construct ResourceBundle but as you are saying your file is huge then what if it is loaded into memory.
update
public class Resource extends java.util.ResourceBundle {
public Object handleGetObject(String key) {
//code
}
public Enumeration getKeys() {
//code
}
}
then for IN locale
import java.util.*;
public class Resource_en_IN extends Resource{
public Object handleGetObject(String key) {
//code
}
}
More Food for thought than a tested solution.
XML files support entities to inline text from other files during parsing. If seen complex xml files, where this technique has been used to modularise the files.
Properties now supports two file formats, the common .properties format with key/value pairs and an xml formats. Properties can load and store to/from xml files.
ResourceBundle has one direct subclass: PropertyResourceBundle. It looks like this class is actually limited to the older key/value pair format, but it could be used to implement another class, like XMLPropertyResourceBundle which is capable of reading properties from xml files where the entity trick could help to modularize those files.
If this works - transforming the existing property files to xml properties files should be easy, just use the Properties class, read from standard format and store to XML.
Related
I am currently making resources for an app that is using ResourceBundle. The thing is, with the current code to dispatch the resources I would need to create an instance of the resource bundle every time I need it and I can guess this is not a good idea, since I would end up loading the resources again and again.
The second solution would be to divide the bundle into many, But I would end up with bundles have only 2-3 strings and like 15 bundles.
My question is:
Is there a way to simple load all the resources in a single static class and access them from there.
I made this little piece of code that seems to work for me but I doubt its quality.
public class StaticBundle
{
private final static ResourceBundle resBundle =
ResourceBundle.getBundle("com.resources");
public final static String STRING_A = resBundle.getString("KEY_A");
public final static String STRING_B = resBundle.getString("KEY_B");
public final static String STRING_C = resBundle.getString("KEY_C");
}
With this I can call StaticBundle.STRING_A and get the value anywhere in the project but since the bundle is initialized at the same time as the class itself... It is highly possible that the program won't have the time to load the proper local from the preferences.
Is there a good way to do this or any other possible solution?
Thank you
If you intend to have only messages for the default locale then what you have is fine.
Alternatively you could let the caller specify which key it needs instead of having constants, like this:
public static String getMessage(String key) {
return resBundle.getString(key);
}
If you like to support multiple locales then the usual approach is to have a Map<Locale, ResourceBundle>Map<Locale, Map<String, String> where you load the resources only once for each locale. In that case your class would have a method where the caller can specify the locale:
public static String getMessage(String key, Locale locale) {
Map<String, String> bundle = bundles.get(locale); // this is the map with all bundles
if (bundle == null) {
// load the bundle for the locale specified
// here you would also need some logic to mark bundles that were not found so
// to avoid continously searching bundles that are not present
// you could even return the message for the default locale if desirable
}
return bundle.get(key);
}
Edit: As correctly pointed out by #JB Nizet (thanks) ResourceBundle already stores a Map. The custom solution I provided in the source example, was about a custom mechanism similar to ResourceBundle that used a Map of Maps to load translations of keys in a property=value format, not only from files but also a database. I have incorrectly thought that we had a Map of ResourceBundle in that solution. The source example is fixed now.
You can create a singleton class:
public class MyResouceBundle extends ResourceBundle {
private static MyResourceBundle instance = new MyResouceBundle();
// private constructor, no one can instantiate this class, only itself
private MyResourceBundle() {
}
public ResourceBundle getInstance() {
return instance;
}
}
Then, everyone will access the same instance of the class with (to get string for KEY_A, for example):
MyResourceBunde.getInstance().get("KEY_A");
This is an easy question to which I can't find a concluding answer.
I can load string properties (e.g.: a query for a prepared statement) from a config.properties file. Let's say I want to take the database connection to which to connect.
If I want to take this information from the file, I could do just the following in a class:
private static final ResourceBundle BUNDLE = ResourceBundle.getBundle("scheduler");
private static final String DRIVER = BUNDLE.getString("bd.driver");
private static final String CONNECTIONURL =BUNDLE.getString("bd.url");
....
But instead I've seen that many people recommend using instead Properties, Then I would have to do the same with something like this (if I want to keep the class static and not have a proper constructor):
static {
prop = new Properties();
try { prop.load(ReportsDB.class.getClassLoader().getResourceAsStream("config.properties"));
} catch (IOException ex) {
Logger.getLogger(ReportsDB.class.getName()).log(Level.SEVERE, null, ex);
throw new RuntimeException(ex);
}
}
private static final String DRIVER = prop.getProperty("bd.driver");
private static final String CONNECTIONURL = prop.getProperty("bd.url");
So, why shouldn’t I use the ResourceBundle instead of Properties when the second one is more verbose?
So, why shouldn’t I use the ResourceBundle instead of Properties when the second one is more verbose?
Because that's not what ResourceBundle is for. The description of the class starts with:
Resource bundles contain locale-specific objects. When your program needs a locale-specific resource, a String for example, your program can load it from the resource bundle that is appropriate for the current user's locale. In this way, you can write program code that is largely independent of the user's locale isolating most, if not all, of the locale-specific information in resource bundles.
Does any of this sound like your use case? I don't think so.
It sounds like the problem is purely the verbosity of loading a properties file: so write a utility method to do that. Then your code can be simply:
private static final Properties CONFIGURATION = PropertyUtil.load("scheduler.properties");
private static final String DRIVER = CONFIGURATION.getString("bd.driver");
private static final String CONNECTIONURL = CONFIGURATION.getString("bd.url");
Admittedly I'm not keen on having static field initializers in an order-dependent way like that... I'd be tempted to encapsulate all of the configuration in a separate class, so you could write:
private static final SchedulerConfiguration CONFIG =
SchedulerConfiguration.load("scheduler.properties");
then use CONFIG.getDriver() etc which could fetch from the properties each time, or use a field, or whatever.
One concrete difference is that ResourceBundle.getBundle("scheduler") will search for the file in the classpath (the src package folder for example). If you call ResourceBundle.getBundle("myfile") on an external file you will get the MissingResourceException.
If you want to use an external file (a file located in the project root for example) you can use the Properties class:
Properties configuration = new Properties();
try (InputStream input = new FileInputStream("configuration.properties")) {
configuration.load(input);
System.out.println("Configuration value: " + configuration.getProperty("key"));
} catch (IOException e) {
e.printStackTrace();
}
I want to store file names, which keep on changing as the new files get added. I am looking for a minimum change in server code later when there is a need to support a new 'file' The thought I have is to store them either in properties file or as Java enum, but still thinking which is a better approach.
I am using REST and having 'file type' in the URL.
Example rest url:
hostname/file-content/TYPE
where value of TYPE could be any of these: standardFileNames1,standardFileNames2,randomFileName1,randomFileName2
I have used TYPE to group the files, so as to minimize the change in url when a new file is added. Dont want to have file names in the URL due to security issues.
my thought goes like this:
having as ENUM:
public enum FileType
{
standardFileNames1("Afile_en", "Afile_jp"),
standardFileNames2("Bfile_en","Bfile_jp"),
randomFileName1("xyz"),
randomFileName2("abc"),
...
...
}
having as properties file:
standardFileNames1=Afile_en,Afile_jp
standardFileNames2=Bfile_en,Bfile_jp
randomFileName1=xyz
randomFileName2=abc
I know having this in properties will save build efforts on every change, but still want to know your views to figure out best solution with all considerations.
Thanks!
Akhilesh
I often use property file + enum combination. Here is an example:
public enum Constants {
PROP1,
PROP2;
private static final String PATH = "/constants.properties";
private static final Logger logger = LoggerFactory.getLogger(Constants.class);
private static Properties properties;
private String value;
private void init() {
if (properties == null) {
properties = new Properties();
try {
properties.load(Constants.class.getResourceAsStream(PATH));
}
catch (Exception e) {
logger.error("Unable to load " + PATH + " file from classpath.", e);
System.exit(1);
}
}
value = (String) properties.get(this.toString());
}
public String getValue() {
if (value == null) {
init();
}
return value;
}
}
Now you also need a property file (I ofter place it in src, so it is packaged into JAR), with properties just as you used in enum. For example:
constants.properties:
#This is property file...
PROP1=some text
PROP2=some other text
Now I very often use static import in classes where I want to use my constants:
import static com.some.package.Constants.*;
And an example usage
System.out.println(PROP1);
Source:http://stackoverflow.com/questions/4908973/java-property-file-as-enum
My suggestion is to keep in properties or config file and write a generic code to get the file list and parse in java. So that whenever a new file comes, there will be no change on server side rather you will add an entry to the properties or config file.
I would like to implement properties policy in my application : i want to define default properties inside my application, and in case of, i want to keep possibility to overriding this default parameters by a config file outside of the war file.
So i define a ConfigModule.java:
public class ConfigModule extends AbstractModule {
private static final Logger LOG = LoggerFactory.getLogger(BatchConfigModule.class);
#Override
protected void configure() {
LOG.info("Start rocoto configuration");
Module rocotoModule = Rocoto.expandVariables(new ConfigurationModule() {
#Override
protected void bindConfigurations() {
// default config
LOG.debug("Default config");
bindProperties(Config.DEFAULT_CONFIG);
LOG.debug("before config.properties");
// // For overriding default config
File propertiesFile = new File(Resources.getResource("config.properties")
.getFile());
if (propertiesFile.exists()) {
LOG.info("config.properties was found in classpath: ["
+ propertiesFile.getAbsolutePath() + "]");
bindProperties(propertiesFile);
} else {
LOG.info("config.properties was not found in classpath");
}
}
});
install(rocotoModule);
}
}
Config.DEFAULT_CONFIG extends java.util.Properties and define default properties, each parameters in DEFAULT_CONFIG is like this =>
DEFAULT_CONFIG.setProperty("testModeInt", "${testMode|false}");
And i inject in my code property with #Named("testModeInt").
My problem is, if my config.properties is not present in classpath, i have an error :
Caused by: java.lang.IllegalStateException: Re-entry not allowed
at com.google.inject.internal.util.$Preconditions.checkState(Preconditions.java:142)
at org.nnsoft.guice.rocoto.configuration.ConfigurationModule.configure(ConfigurationModule.java:63)
at com.google.inject.AbstractModule.configure(AbstractModule.java:59)
at com.google.inject.spi.Elements$RecordingBinder.install(Elements.java:223)
at com.google.inject.spi.Elements.getElements(Elements.java:101)
at com.google.inject.spi.Elements.getElements(Elements.java:92)
at com.google.inject.util.Modules$RealOverriddenModuleBuilder$1.configure(Modules.java:152)
at com.google.inject.AbstractModule.configure(AbstractModule.java:59)
at com.google.inject.spi.Elements$RecordingBinder.install(Elements.java:223)
at com.google.inject.AbstractModule.install(AbstractModule.java:118)
at net.antoine.ConfigModule.configure(ConfigModule.java:51)
Which i just don't understand, where is this problem come from, or maybe this implementation is not good, another idea ?
You can try OWNER API: it supports 3 levels of properties overriding, and it also provides mapping between a Java object and properties files and other neat features.
The first overriding capability is the specification of the default value on the mapping interface:
public interface ServerConfig extends Config {
#DefaultValue("42")
int maxThreads();
}
ServerConfig gets mapped by default to ServerConfig.properties in the same package. If the property "maxThreads" is not specified, then 42 is used as default (the properties files overrides the DefaultValue).
The second overriding capability is the possibility to specify multiple properties file locations for the class, so the first resource found is used. This way you define an internal properties in your jar, and allow the user to specify an overriding properties file in his home directory or in /etc/myapp, or everywhere else you prefer:
#Sources({ "file:~/.myapp.config",
"file:/etc/myapp.config",
"classpath:foo/bar/baz.properties" })
public interface ServerConfig extends Config {
#Key("server.http.port")
int port();
#Key("server.host.name")
String hostname();
#Key("server.max.threads");
#DefaultValue("42")
int maxThreads();
}
The third overriding capability is to specify that you want all of the above file being considered but you want the override to happen to a property level, so that if all the above
properties file are available, when you ask for the property maxThreads(), this will be searched first in file:~/.myapp.config - if not found - then in file:/etc/myapp.config and then in classpath:foo/bar/baz.properties, and as last resort the #DefaultValue("42") applies, then you specify to the library that it has to produce a merge of all the properties resources (and consider them all)
#LoadPolicy(LoadType.MERGE)
#Sources({ "file:~/.myapp.config",
"file:/etc/myapp.config",
"classpath:foo/bar/baz.properties" })
public interface ServerConfig extends Config {
// same content as above.
}
I have an exsisting Java/JSF website all the text on the screen is coming from property files via <f:loadBundle basename="org.example.applicaltion" var="applicaltion" /> which pulls the text from applicaltion.properties.
For a runtime configurable subset of these I am wanting to pull the string from else where (CMS via web services). Looking at the ResourceBundle class it appares to have an infrastructer for something close to this with delegation to a parent ResourceBundle.
I am wanting somethis like this
public class Applicaltion extends ResourceBundle{
#Override
protected Object handleGetObject(String key) {
if(overridenKey(key)){
return overridedValue(key);
}
return null; // ResourceBundle.getObject will delegate to parent
// if we return null
}
}
I have tried this and parent is null, I assume this is more used for the case of default -> en -> en_GB.
I am considering the not very appealing option of have the property file a different name from the custom resourceBundle and then delegating through the full stack of ResourceBundle.getBundle(PROPERTY_FILE_NAME).getString(key) from within CustomResourceBundle.handleGetObject(key).
Any better ideas?
I ended up solving this by checking if we had an override value, if we did returning that, else delegating to the standard resource bundle
public class UILabels extends ResourceBundle {
private ResourceBundle getFileResources(){
return ResourceBundle.getBundle("com.example.web.UILabelsFile", this.getLocale());
}
public Enumeration<String> getKeys() {
return getFileResources().getKeys();
}
protected Object handleGetObject(String key) {
if(overrideValue(key)){
return getOverridenValue(key);
}
return getFileResources().getObject(key);
}
}
Note the slight difference in name class is UILabels which is what all clients will use the file is UILabelsFile so the ResourceBundle loader does not go recursive.
You could write a custom PropertyResolver, and then have that perform the logic of where to pull the property values from.
For example, you could define a bean called messageSource and have that load up application.properties, plus your CMS properties or whatever you have.
Then write a custom PropertyResolver (there's an example of how to do this with Spring's MessageSource here) and link it in to your faces-config.xml using something like this:
<application>
<property-resolver>
com.package.MessageSourcePropertyResolver
</property-resolver>
</application>