How to implement overriding properties policy with rocoto ? - java

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.
}

Related

JAAS: How to load config file?

I'm trying to run the JAAS code sample.
lc = new LoginContext("JaasSample", new TextCallbackHandler());
To register the configuration file I try the command:
java -Djava.security.auth.login.config=jaas.config JaasAcn
And gets the error:
Error: Could not find or load main class .security.auth.login.config=jaas.config
Caused by: java.lang.ClassNotFoundException: /security/auth/login/config=jaas/config
Firstly write javax instead of java, so:
java -Djavax.security.auth.login.config=jaas.config JaasAcn
And second, consider to config it programmicly:
Inherit the javax.security.auth.login.Configuration class.
Override the function AppConfigurationEntry[] getAppConfigurationEntry(String name). In this func you can return an AppConfigurationEntry object that represent a row in config file.
new AppConfigurationEntry(NTLoginModule.class.getName(),
AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, new HashMap<>())
With name parameter you can responde for the name parameter in LoginContext constructor.
Create object from your Configuration class and put him in Configuration:
MyConfiguration config = new MyConfiguration();
Configuration.setConfiguration(config);
The shortened code can look like this:
Configuration config = new Configuration() {
#Override
public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
return new AppConfigurationEntry[]{
new AppConfigurationEntry(NTLoginModule.class.getName(),
AppConfigurationEntry.LoginModuleControlFlag.REQUIRED,
new HashMap<>())
};
}
};
Configuration.setConfiguration(config);
Clearly there is a typo in the command line: a space after -Djava, so the .security.auth.login.config=jaas.config part is taken as the class name (and hence also mangled accordingly).

Creating MBean in Java

I am trying to make a class implement an MBean Interface so I can interrogate the properties at runtime. The class I am trying to interrogate is as follows
public class ProfileCache implements ProfileCacheInterfaceMBean{
private Logger logger = Logger.getLogger(ProfileCache.class);
private ConcurrentMap<String, Profile> cache;
public ProfileCache(ConcurrentMap<String, Profile> cache){
this.cache = cache;
}
/**
* Update the cache entry for a given user id
* #param userid the user id to update for
* #param profile the new profile to store
* #return true if the cache update
*/
public boolean updateCache(String userid, Profile profile) {
if (cache == null || cache.size() == 0) {
throw new RuntimeException("Unable to update the cache");
}
if (cache.containsKey(userid)) {
if (profile != null) {
cache.put(userid, profile);
logger.info("Updated the cache for user: "
+ userid + " profile: " + profile);
return true;
}
}
return false;
}
#Override
public ConcurrentMap<String, Profile> getCache() {
if(cache == null){
cache = new ConcurrentHashMap<String, Profile>();
}
return cache;
}
}
The interface looks like this
import com.vimba.profile.Profile;
public interface ProfileCacheInterfaceMBean {
ConcurrentMap<String, Profile> getCache();
}
And i start the MBean like this
cacheImpl = new ProfileCache(factory.createCacheFromDB());
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
ObjectName profileCache = new ObjectName("org.javalobby.tnt.jmx:type=ProfileCacheInterfaceMBean");
mbs.registerMBean(cacheImpl, profileCache);
However i keep getting the below exception and I am not sure what I need to change
javax.management.NotCompliantMBeanException: MBean class com.vimba.cache.ProfileCache does not implement DynamicMBean, and neither follows the Standard MBean conventions (javax.management.NotCompliantMBeanException: Class com.vimba.cache.ProfileCache is not a JMX compliant Standard MBean) nor the MXBean conventions (javax.management.NotCompliantMBeanException: com.vimba.cache.ProfileCache: Class com.vimba.cache.ProfileCache is not a JMX compliant MXBean)
I think potentially it's because it returns a Map?
Having just encountered this exception and looked at the current answers as well as https://blogs.oracle.com/jmxetc/entry/javax_management_standardmbean_when_and I thought it might help to emphasize and clarify the following already elucidated to:
The NotCompliantMBeanException is caused, among other things, by failing to follow this convention 'ConcreteClassName' implements 'ConcreteClassNameMBean'
I resolved this by updating the original name of my mbean interface from 'OrignalNameMBean' to 'OriginalNameMXBean' allowing the mbean to be registered without following the convention
Another solution would be to follow the convention.
I had the same issue ("does not implement DynamicMBean, and neither follows the Standard MBean conventions") and this article helped me to resolve the problem (see Using StandardMBean section: https://blogs.oracle.com/jmxetc/entry/javax_management_standardmbean_when_and).
I have to explicitly construct a
StandardMBean mbean = new StandardMBean(mBeanImpl, MBeanInterface.class);
then register the mbean:
mbServer.registerMBean(mbean, mBeanName);
It works.
When I register the mBeanImpl with the mbServer, I got the above exception.
The implementing mbean class can declare as many methods as it likes that are not defined in mbeans interface... There is no requirement that the implementing class can/must only implement the interface methods.
In many of the cases this problem is caused because the mbean interface and implementation class are not in the same package!
You can change interface name from SomethingMBean to SomethingMXBean,such as HelloMBean to HelloMXBean,from jdk's source code i saw this:
public static boolean isMXBeanInterface(Class<?> interfaceClass) {
if (!interfaceClass.isInterface())
return false;
if (!Modifier.isPublic(interfaceClass.getModifiers()) &&
!Introspector.ALLOW_NONPUBLIC_MBEAN) {
return false;
}
MXBean a = interfaceClass.getAnnotation(MXBean.class);
if (a != null)
return a.value();
return interfaceClass.getName().endsWith("MXBean");
}
if not endsWith "MXBean",it will return false,then cause throw IllegalArgumentException
jdk version:1.8.0_25
class is "JMX",line 376
Just change interface suffix from 'MBean' to 'MXBean'.
This works for me.
Just change your implementation class name from ProfileCache to ProfileCacheInterface. It should work now. Moreover your implementation class can have any number of its own methods and those methods needs not to be mentioned in the MBean interface.
JMX's standard mbean naming convention is like this
public interface SomeBeanNameMBean{
...
}
public class SomeBeanName implements SomeBeanNameMBean{
...
//implements all the methods of SomeBeanNameMBean
...
//implement other class's own methods if needed
}
I faced the same problem like 'Exception in thread "main" javax.management.NotCompliantMBeanException', in my case I kept MBean interface and implementation classes in differnet package. To resolve the issue, I moved both MBean interface and the implementation class to same package.
In the all the examples I've seen for MBean implementations, I've never seen a class define a method that was not defined in the interface. E.g., ProfileCache has method updateCache, but ProfileCacheInterfaceMBean does not. Try removing the updateCache method from ProfileCache and see if it will work.

Java - How to find all direct subclasses of a class with reflection? [duplicate]

How does one go about and try to find all subclasses of a given class (or all implementors of a given interface) in Java?
As of now, I have a method to do this, but I find it quite inefficient (to say the least).
The method is:
Get a list of all class names that exist on the class path
Load each class and test to see if it is a subclass or implementor of the desired class or interface
In Eclipse, there is a nice feature called the Type Hierarchy that manages to show this quite efficiently.
How does one go about and do it programmatically?
Scanning for classes is not easy with pure Java.
The spring framework offers a class called ClassPathScanningCandidateComponentProvider that can do what you need. The following example would find all subclasses of MyClass in the package org.example.package
ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false);
provider.addIncludeFilter(new AssignableTypeFilter(MyClass.class));
// scan in org.example.package
Set<BeanDefinition> components = provider.findCandidateComponents("org/example/package");
for (BeanDefinition component : components)
{
Class cls = Class.forName(component.getBeanClassName());
// use class cls found
}
This method has the additional benefit of using a bytecode analyzer to find the candidates which means it will not load all classes it scans.
There is no other way to do it other than what you described. Think about it - how can anyone know what classes extend ClassX without scanning each class on the classpath?
Eclipse can only tell you about the super and subclasses in what seems to be an "efficient" amount of time because it already has all of the type data loaded at the point where you press the "Display in Type Hierarchy" button (since it is constantly compiling your classes, knows about everything on the classpath, etc).
This is not possible to do using only the built-in Java Reflections API.
A project exists that does the necessary scanning and indexing of your classpath so you can get access this information...
Reflections
A Java runtime metadata analysis, in the spirit of Scannotations
Reflections scans your classpath, indexes the metadata, allows you to query it on runtime and may save and collect that information for many modules within your project.
Using Reflections you can query your metadata for:
get all subtypes of some type
get all types annotated with some annotation
get all types annotated with some annotation, including annotation parameters matching
get all methods annotated with some
(disclaimer: I have not used it, but the project's description seems to be an exact fit for your needs.)
Try ClassGraph. (Disclaimer, I am the author). ClassGraph supports scanning for subclasses of a given class, either at runtime or at build time, but also much more. ClassGraph can build an abstract representation of the entire class graph (all classes, annotations, methods, method parameters, and fields) in memory, for all classes on the classpath, or for classes in selected packages, and you can query this class graph however you want. ClassGraph supports more classpath specification mechanisms and classloaders than any other scanner, and also works seamlessly with the new JPMS module system, so if you base your code on ClassGraph, your code will be maximally portable. See the API here.
Don't forget that the generated Javadoc for a class will include a list of known subclasses (and for interfaces, known implementing classes).
I know I'm a few years late to this party, but I came across this question trying to solve the same problem. You can use Eclipse's internal searching programatically, if you're writing an Eclipse Plugin (and thus take advantage of their caching, etc), to find classes which implement an interface. Here's my (very rough) first cut:
protected void listImplementingClasses( String iface ) throws CoreException
{
final IJavaProject project = <get your project here>;
try
{
final IType ifaceType = project.findType( iface );
final SearchPattern ifacePattern = SearchPattern.createPattern( ifaceType, IJavaSearchConstants.IMPLEMENTORS );
final IJavaSearchScope scope = SearchEngine.createWorkspaceScope();
final SearchEngine searchEngine = new SearchEngine();
final LinkedList<SearchMatch> results = new LinkedList<SearchMatch>();
searchEngine.search( ifacePattern,
new SearchParticipant[]{ SearchEngine.getDefaultSearchParticipant() }, scope, new SearchRequestor() {
#Override
public void acceptSearchMatch( SearchMatch match ) throws CoreException
{
results.add( match );
}
}, new IProgressMonitor() {
#Override
public void beginTask( String name, int totalWork )
{
}
#Override
public void done()
{
System.out.println( results );
}
#Override
public void internalWorked( double work )
{
}
#Override
public boolean isCanceled()
{
return false;
}
#Override
public void setCanceled( boolean value )
{
}
#Override
public void setTaskName( String name )
{
}
#Override
public void subTask( String name )
{
}
#Override
public void worked( int work )
{
}
});
} catch( JavaModelException e )
{
e.printStackTrace();
}
}
The first problem I see so far is that I'm only catching classes which directly implement the interface, not all their subclasses - but a little recursion never hurt anyone.
I did this several years ago. The most reliable way to do this (i.e. with official Java APIs and no external dependencies) is to write a custom doclet to produce a list that can be read at runtime.
You can run it from the command line like this:
javadoc -d build -doclet com.example.ObjectListDoclet -sourcepath java/src -subpackages com.example
or run it from ant like this:
<javadoc sourcepath="${src}" packagenames="*" >
<doclet name="com.example.ObjectListDoclet" path="${build}"/>
</javadoc>
Here's the basic code:
public final class ObjectListDoclet {
public static final String TOP_CLASS_NAME = "com.example.MyClass";
/** Doclet entry point. */
public static boolean start(RootDoc root) throws Exception {
try {
ClassDoc topClassDoc = root.classNamed(TOP_CLASS_NAME);
for (ClassDoc classDoc : root.classes()) {
if (classDoc.subclassOf(topClassDoc)) {
System.out.println(classDoc);
}
}
return true;
}
catch (Exception ex) {
ex.printStackTrace();
return false;
}
}
}
For simplicity, I've removed command line argument parsing and I'm writing to System.out rather than a file.
Keeping in mind the limitations mentioned in the other answers, you can also use openpojo's PojoClassFactory (available on Maven) in the following manner:
for(PojoClass pojoClass : PojoClassFactory.enumerateClassesByExtendingType(packageRoot, Superclass.class, null)) {
System.out.println(pojoClass.getClazz());
}
Where packageRoot is the root String of the packages you wish to search in (e.g. "com.mycompany" or even just "com"), and Superclass is your supertype (this works on interfaces as well).
Depending on your particular requirements, in some cases Java's service loader mechanism might achieve what you're after.
In short, it allows developers to explicitly declare that a class subclasses some other class (or implements some interface) by listing it in a file in the JAR/WAR file's META-INF/services directory. It can then be discovered using the java.util.ServiceLoader class which, when given a Class object, will generate instances of all the declared subclasses of that class (or, if the Class represents an interface, all the classes implementing that interface).
The main advantage of this approach is that there is no need to manually scan the entire classpath for subclasses - all the discovery logic is contained within the ServiceLoader class, and it only loads the classes explicitly declared in the META-INF/services directory (not every class on the classpath).
There are, however, some disadvantages:
It won't find all subclasses, only those that are explicitly declared. As such, if you need to truly find all subclasses, this approach may be insufficient.
It requires the developer to explicitly declare the class under the META-INF/services directory. This is an additional burden on the developer, and can be error-prone.
The ServiceLoader.iterator() generates subclass instances, not their Class objects. This causes two issues:
You don't get any say on how the subclasses are constructed - the no-arg constructor is used to create the instances.
As such, the subclasses must have a default constructor, or must explicity declare a no-arg constructor.
Apparently Java 9 will be addressing some of these shortcomings (in particular, the ones regarding instantiation of subclasses).
An Example
Suppose you're interested in finding classes that implement an interface com.example.Example:
package com.example;
public interface Example {
public String getStr();
}
The class com.example.ExampleImpl implements that interface:
package com.example;
public class ExampleImpl implements Example {
public String getStr() {
return "ExampleImpl's string.";
}
}
You would declare the class ExampleImpl is an implementation of Example by creating a file META-INF/services/com.example.Example containing the text com.example.ExampleImpl.
Then, you could obtain an instance of each implementation of Example (including an instance of ExampleImpl) as follows:
ServiceLoader<Example> loader = ServiceLoader.load(Example.class)
for (Example example : loader) {
System.out.println(example.getStr());
}
// Prints "ExampleImpl's string.", plus whatever is returned
// by other declared implementations of com.example.Example.
It should be noted as well that this will of course only find all those subclasses that exist on your current classpath. Presumably this is OK for what you are currently looking at, and chances are you did consider this, but if you have at any point released a non-final class into the wild (for varying levels of "wild") then it is entirely feasible that someone else has written their own subclass that you will not know about.
Thus if you happened to be wanting to see all subclasses because you want to make a change and are going to see how it affects subclasses' behaviour - then bear in mind the subclasses that you can't see. Ideally all of your non-private methods, and the class itself should be well-documented; make changes according to this documentation without changing the semantics of methods/non-private fields and your changes should be backwards-compatible, for any subclass that followed your definition of the superclass at least.
The reason you see a difference between your implementation and Eclipse is because you scan each time, while Eclipse (and other tools) scan only once (during project load most of the times) and create an index. Next time you ask for the data it doesn't scan again, but look at the index.
I'm using a reflection lib, which scans your classpath for all subclasses: https://github.com/ronmamo/reflections
This is how it would be done:
Reflections reflections = new Reflections("my.project");
Set<Class<? extends SomeType>> subTypes = reflections.getSubTypesOf(SomeType.class);
You can use org.reflections library and then, create an object of Reflections class. Using this object, you can get list of all subclasses of given class.
https://www.javadoc.io/doc/org.reflections/reflections/0.9.10/org/reflections/Reflections.html
Reflections reflections = new Reflections("my.project.prefix");
System.out.println(reflections.getSubTypesOf(A.class)));
Add them to a static map inside (this.getClass().getName()) the parent classes constructor (or create a default one) but this will get updated in runtime. If lazy initialization is an option you can try this approach.
I just write a simple demo to use the org.reflections.Reflections to get subclasses of abstract class:
https://github.com/xmeng1/ReflectionsDemo
I needed to do this as a test case, to see if new classes had been added to the code. This is what I did
final static File rootFolder = new File(SuperClass.class.getProtectionDomain().getCodeSource().getLocation().getPath());
private static ArrayList<String> files = new ArrayList<String>();
listFilesForFolder(rootFolder);
#Test(timeout = 1000)
public void testNumberOfSubclasses(){
ArrayList<String> listSubclasses = new ArrayList<>(files);
listSubclasses.removeIf(s -> !s.contains("Superclass.class"));
for(String subclass : listSubclasses){
System.out.println(subclass);
}
assertTrue("You did not create a new subclass!", listSubclasses.size() >1);
}
public static void listFilesForFolder(final File folder) {
for (final File fileEntry : folder.listFiles()) {
if (fileEntry.isDirectory()) {
listFilesForFolder(fileEntry);
} else {
files.add(fileEntry.getName().toString());
}
}
}
If you intend to load all subclassess of given class which are in the same package, you can do so:
public static List<Class> loadAllSubClasses(Class pClazz) throws IOException, ClassNotFoundException {
ClassLoader classLoader = pClazz.getClassLoader();
assert classLoader != null;
String packageName = pClazz.getPackage().getName();
String dirPath = packageName.replace(".", "/");
Enumeration<URL> srcList = classLoader.getResources(dirPath);
List<Class> subClassList = new ArrayList<>();
while (srcList.hasMoreElements()) {
File dirFile = new File(srcList.nextElement().getFile());
File[] files = dirFile.listFiles();
if (files != null) {
for (File file : files) {
String subClassName = packageName + '.' + file.getName().substring(0, file.getName().length() - 6);
if (! subClassName.equals(pClazz.getName())) {
subClassList.add(Class.forName(subClassName));
}
}
}
}
return subClassList;
}
find all classes in classpath
public static List<String> getClasses() {
URLClassLoader urlClassLoader = (URLClassLoader) Thread.currentThread().getContextClassLoader();
List<String> classes = new ArrayList<>();
for (URL url : urlClassLoader.getURLs()) {
try {
if (url.toURI().getScheme().equals("file")) {
File file = new File(url.toURI());
if (file.exists()) {
try {
if (file.isDirectory()) {
for (File listFile : FileUtils.listFiles(file, new String[]{"class"}, true)) {
String classFile = listFile.getAbsolutePath().replace(file.getAbsolutePath(), "").replace(".class", "");
if (classFile.startsWith(File.separator)) {
classFile = classFile.substring(1);
}
classes.add(classFile.replace(File.separator, "."));
}
} else {
JarFile jarFile = new JarFile(file);
if (url.getFile().endsWith(".jar")) {
Enumeration<JarEntry> entries = jarFile.entries();
while (entries.hasMoreElements()) {
JarEntry jarEntry = entries.nextElement();
if (jarEntry.getName().endsWith(".class")) {
classes.add(jarEntry.getName().replace(".class", "").replace("/", "."));
}
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
} catch (URISyntaxException e) {
e.printStackTrace();
}
}
return classes;
}
enter link description hereService Manager in java will get all implementing classes for an interface in J

Is it possible to include resource bundle files within a resource bundle

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.

How to override some Resources from a PropertyResourceBundle

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>

Categories