Managing Google Guice Properties

I would like to create the right property management strategy in java webapp that relays on google guice as a DI infrastructure.

I would like to have a mechanism that meets the following three requirements:

  • I would like to be able to enter properties using guice (@Named)
  • I would like to have access to properties in a static way.
  • The mechanism must support property prioritization, which means that the property can be wrapped in a deployed war with a certain value, but it can also be redundant at the target system level or local file system (on the target machine that I will deploy to), in which case the value in war will be overridden by the value that exists on the target machine.

I believe this is a standard requirement. Now, using a standard binder potion, I can easily get the first requirement, but not the other two. To get the other two, I created my own class that does the following:

  • Wraps around and provides guice binding methods (those that bind properties) For example:

public static void bindString(AnnotatedBindingBuilder<String> binder, String property, String defaultValue) { binder.annotatedWith(Names.named(property)).toInstance(getProperty(property, defaultValue)); }

Where the getProperty method knows how to handle my properties (get the value from a war or system level), and also provides properties statically.

So basically, while I use this utility that I created for property bindings, I'm good, it covers all my requirements, but as soon as I use standard guice bindings, I lose the second and third requirements.

Is there a way to override the guice bindings and get all these 3 requirements?

Once I had the same call in a spring application and it was pretty easy. I applied ApplicationContextInitializer as follows:

@Override public void initialize(ConfigurableWebApplicationContext ctx) { PropertySource<Map<String, Object>> localProps = null; try { localProps = new ResourcePropertySource(new ClassPathResource(LOCAL_PROPERTIES_FILE_NAME)); } catch (IOException e) { LOG.fatal("Could not load local properties from classpath " + LOCAL_PROPERTIES_FILE_NAME); return; } LOG.info("Loaded configuration from classpath local file " + LOCAL_PROPERTIES_FILE_NAME); ctx.getEnvironment().getPropertySources().addFirst(localProps); }

so this gave me a way to add local properties with the highest priority for my environment. In case of coincidence with the properties of war, local ones have a higher priority. In addition, I set my environment statically, so I have static access to my properties (for services that are not controlled by the container, mostly mainly).

How can I achieve this with guice?

+7
java properties dependency-injection guice
source share
1 answer

Unfortunately, I donโ€™t think you will find anything that will give you a really clean and satisfying implementation. Especially, I donโ€™t think that you will find anything that will give you exactly what you want without realizing at least part of it.

If I had these needs, I would make sure that my injector was created in a central InjectorFactory. If creating an injector requires a large number of parameters from the outside, I would simply create it once at the very beginning of my application, and then cache the injector in a static final field. This will make it available to the static method. I would bind the loading of the "fall-back" property to an explicit provider. Thus, instead of using the standard method Names.bindProperties (...) I would bind it directly to the Provider. This provider then implements the logic necessary to back up or merge multiple property files. Having an injector cached in a static field means that I can call a static method to access properties from the global context outside my classes entered.

Using your own provider seems initially unpleasant, but may provide additional benefits. For starters, you can implement your backup strategy exactly the way you want. In addition, you can add additional types of behavior, such as automatically reloading your property files, etc. (Not shown in my sample code).

 public class InjectorFactory { private static Injector injector = null; public static synchronized Injector getOrCreateInjector() { if(injector == null) { injector = Guice.createInjector(new AbstractModule() { @Override protected void configure() { Properties properties1 = createProperties("file1.properties"); Properties properties2 = createProperties("file2.properties"); Set<Object> propertyNames = new HashSet<Object>(); propertyNames.addAll(properties1.keySet()); propertyNames.addAll(properties2.keySet()); for (Object object : propertyNames) { String propertyName = (String) object; bind(String.class).annotatedWith(Names.named(propertyName)).toProvider(new StringProvider(properties1, properties2, propertyName)); } } private Properties createProperties(String propertyFileName) { try { InputStream stream = InjectorFactory.class.getResourceAsStream(propertyFileName); try { Properties properties = new Properties(); properties.load(stream); return properties; } finally { stream.close(); } } catch (IOException exception) { throw new RuntimeException("Could not load properties file"); } } }); } return injector; } public static String getProperty(String propertyName) { return getOrCreateInjector().getInstance(Key.get(String.class, Names.named(propertyName))); } } 

Given the codes above and file1.properties:

 property1=Property1Value property2=Property2Value 

And file.properties:

 property2=IncorrectProperty2Value property3=Property3Value 

with provider

 public class StringProvider implements Provider<String> { private Properties properties1; private Properties properties2; private String propertyName; public StringProvider(Properties properties1, Properties properties2, String propertyName) { this.properties1 = properties1; this.properties2 = properties2; this.propertyName = propertyName; } public String get() { if(properties1.containsKey(propertyName)) { return properties1.getProperty(propertyName); } return properties2.getProperty(propertyName); } } 

Next use:

 public class InjectorFactoryTest { public static void main(String ... parameters) { System.out.println(InjectorFactory.getProperty("property1")); System.out.println(InjectorFactory.getProperty("property2")); System.out.println(InjectorFactory.getProperty("property3")); } } 

Outputs:

 Property1Value Property2Value Property3Value 
+1
source share

All Articles