How to pass a Map<String, String> with application.properties - spring

I have implemented some authorization in a webservice that needs be configured.
Currently the user/password combo is hardcoded into the bean configuration. I would like to configure the map with users and passwords into the application.properties so the configuration can be external.
Any clue on how this can be done?
<bean id="BasicAuthorizationInterceptor" class="com.test.BasicAuthAuthorizationInterceptor">
<property name="users">
<map>
<entry key="test1" value="test1"/>
<entry key="test2" value="test2"/>
</map>
</property>
</bean>

you can use #Value.
Properties file:
users={test1:'test1',test2:'test2'}
Java code:
#Value("#{${users}}")
private Map<String,String> users;

You can use #ConfigurationProperties to have values from application.properties bound into a bean. To do so you annotate your #Bean method that creates the bean:
#Bean
#ConfigurationProperties
public BasicAuthAuthorizationInterceptor interceptor() {
return new BasicAuthAuthorizationInterceptor();
}
As part of the bean's initialisation, any property on BasicAuthAuthorizationInterceptor will be set based on the application's environment. For example, if this is your bean's class:
public class BasicAuthAuthorizationInterceptor {
private Map<String, String> users = new HashMap<String, String>();
public Map<String, String> getUsers() {
return this.users;
}
}
And this is your application.properties:
users.alice=alpha
users.bob=bravo
Then the users map will be populated with two entries: alice:alpha and bob:bravo.
Here's a small sample app that puts this all together:
import java.util.HashMap;
import java.util.Map;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
#EnableAutoConfiguration
#EnableConfigurationProperties
public class Application {
public static void main(String[] args) throws Exception {
System.out.println(SpringApplication.run(Application.class, args)
.getBean(BasicAuthAuthorizationInterceptor.class).getUsers());
}
#Bean
#ConfigurationProperties
public BasicAuthAuthorizationInterceptor interceptor() {
return new BasicAuthAuthorizationInterceptor();
}
public static class BasicAuthAuthorizationInterceptor {
private Map<String, String> users = new HashMap<String, String>();
public Map<String, String> getUsers() {
return this.users;
}
}
}
Take a look at the javadoc for ConfigurationProperties for more information on its various configuration options. For example, you can set a prefix to divide your configuration into a number of different namespaces:
#ConfigurationProperties(prefix="foo")
For the binding to work, you'd then have to use the same prefix on the properties declared in application.properties:
foo.users.alice=alpha
foo.users.bob=bravo

A java.util.Properties object is already a Map, actually a HashTable which in turn implements Map.
So when you create a properties file (lets name it users.properties) you should be able to load it using a PropertiesFactoryBean or <util:properties /> and inject it into your class.
test1=test1
test2=test2
Then do something like
<util:properties location="classpath:users.properties" id="users" />
<bean id="BasicAuthorizationInterceptor" class="com.test.BasicAuthAuthorizationInterceptor">
<property name="users" ref="users" />
</bean>
Although if you have a Map<String, String> as a type of the users property it might fail... I wouldn't put them in the application.properties file. But that might just be me..

I think you are looking for something similar
http://www.codejava.net/frameworks/spring/reading-properties-files-in-spring-with-propertyplaceholderconfigurer-bean
You can pick values from .properties similarly and assign it to your map.

Related

Autowiring Issue with using Springboot PropertiesFactoryBean

I am new to Springboot PropertiesFactoryBean and want to inject a file from classpath so that I can populate it into a Map
Location of properties file on Eclipse: src/main/resources
contents of File: simple_property.properties:
key1=value1
key2=value2
key3=value3
My ApplicationConfiguration.java looks as below:
import org.springframework.beans.factory.config.PropertiesFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
#Configuration
public class ApplicationConfiguration {
#Bean(name = "simpleMapping")
public static PropertiesFactoryBean artifactMapping() {
PropertiesFactoryBean bean = new PropertiesFactoryBean();
bean.setLocation(new ClassPathResource("simple_property.properties"));
return bean;
}
}
I have a ApplicationRunner interface for bean to be run:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class pCli implements ApplicationRunner {
#Autowired
private SomeClass someProgram;
public static void main(String[] args) {
SpringApplication.run(pCli.class, args);
}
#Override
public void run(ApplicationArguments args) throws Exception {
someProgram.run(args.getOptionValues("param1").get(0));
}
}
I am unable to understand & proceed ahead how I can use bean to read all properties ? Example how I can get the properties into a variable of Map and access them ? (If #Autowired has already loaded the properties file from classpath then how can I access it ? )
Say you have your map in the property file like this,
app.number-map={KEY1:1, KEY2:2, KEY3:3}
You can use this value by injecting the property using the #Value annotation. Following is an example where the value is injected to a method. Spring expression #{${app.number-map}} will fetch the value in the properties file.
someMethod(#Value("#{${app.number-map}}")
Map<String, Integer> numberMap) {
// ...
}
Use application.properties since you're still learning. It'll make your life easy. Also, keeping a separate configuration bean would really help you to manage and access property values easily.

Autowire working in unit test but not in main java class

I've a domain class that I want to auto-populate from external config. Here is my domain class:
#Data
#Configuration
#PropertySource("classpath:application.properties")
public class StudioVo {
#Value("${studio.code}")
private code;
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
Here is my context xml:
<bean class="org.springframework.batch.core.scope.StepScope" />
<bean id="ItemReader" class="com.sdm.studio.reader.StudioReader" scope="step">
<property name="studioVo" ref="StudioVo" />
</bean>
<bean id="StudioConfigVo" class="com.sdm.studio.domain.StudioVo" />
</bean>
Here is the class where I want to use the vo:
#Slf4j
#Data
public class StudioReader implements ItemReader<List<Studio>> {
private StudioVo studioVo;
public List<Studio> read() throws Exception {
System.out.println("getCode: " + studioVo.getCode()); //code is null here
return null;
}
}
However when I run it via unit test by autowiring, it runs fine. Like this:
#RunWith(SpringRunner.class)
#SpringBootTest
public class StudioTest {
#Autowired
private StudioVo studioVo;
#Test
public void testAutoPopulationOfStudio(){
System.out.println("getCode: "+ studioVo.getCode()); // works!
// Assert.assertTrue(studioVo.getCode().equals("102"));
}
}
Not sure what's going on here - I'm working with an old Spring Batch application wrapped in Spring Boot (so there is a mix of XML based and Java based config - and may be that is the cause of this issue). What am I missing?
In your StudioTest, you are autowiring StudioReader where as you missed the #Autowired in your StudioReader code, so add it as shown below:
#Slf4j
#Data
public class StudioReader implements ItemReader<List<Studio>> {
#Autowired //add this so that studioVo can be injected
private StudioVo studioVo;
//add other code
}
Please be certain to note that using #Autowire requires a chain of Spring-managed beans below it from wherever you are using it including the class in which you are using #Autowire. That is because Spring needs the precedent references to match up the object-reference hierarchy. I.e., in business logic layer ClassA, you want to #Autowire a field. ClassA itself needs to be a managed bean. Further, if the field you want to #Autowire holds an object that has referential dependencies to other objects (and most do), these also must be Spring-managed.
For example, the following will work:
package com.example.demo;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MessageRunner {
private static SetterMessage setterMessage;
public static void main(String[] args) {
setterMessage = (SetterMessage) (new AnnotationConfigApplicationContext(DemoConfiguration.class)).getBean("setterMessage");
setterMessage.setMessage("Finally it works.");
p(setterMessage.getMessage());
}
private static void p(String s) {
System.out.println(s);
}
}
DemoConfiguration.java looks like this:
package com.example.demo;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
#Configuration
#ComponentScan(basePackages = "com.example.demo")
public class DemoConfiguration {
}
SetterMessage.java, this:
package com.example.demo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
#Service
#Scope("prototype")
public class SetterMessage {
private String message = null;
#Autowired
private SetterMessage2 setterMessage2;
public String getMessage(){
return message+setterMessage2.getSubMessage();
}
public void setMessage(String message) {
this.message = message;
setterMessage2.setSubMessage("("+message+")");
}
}
SetterMessage2.java:
package com.example.demo;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
#Service
#Scope("prototype")
public class SetterMessage2 {
private String subMsg = "";
public void setSubMessage(String msg) {
subMsg = msg;
}
public String getSubMessage() {
return subMsg;
}
}
Note that SetterMessage2.java is annotated as a Component (#Service) but no field in it is autowired. That is because it's the end of the object reference chain. But because it is a Component, it can be autowired into SetterMessage.java. However look at MessageRunner.java's main() method and field declarations. Note that the class field SetterMessage is NOT autowired. If it were annotated as #Autowired, main() would fail at runtime, throwing an NPE with the reference to setterMessage in main(). This is because MessageRunner.java is not marked as some kind of component. So we need to grab a valid instance of MessageSetter from the application context and use it.
To emphasize, the following version of MessageRunner.java's main() method WILL FAIL, throwing an NPE, if MessageRunner.java looked like this:
...
public class MessageRunner {
#Autowired // <-- This will not do the job for us
private static SetterMessage setterMessage;
public static void main(String[] args) {
setterMessage.setMessage("Finally it works."); // NPE here on ref to setterMessage
p(setterMessage.getMessage());
}
...
This is a real gotchya for people new to Spring. In fact, I'd place it among the Top Five Spring Newbie Discouragers and a really evil, pernicious detail that has caused new Spring programmers countless hours in aggravation and Google searches. So I do hope that noting this phenom here will save at least some newbies time and high blood pressure spikes.
Note: If you go to create the above classes in your IDE, bear in mind these were developed with Spring Boot enabled.

Spring configuration class to inject bean as property

I am having a spring configuration class which I am using to read from properties file and create the beans.
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
#Configuration
#PropertySource("classpath:conf.properties")
public class ApplicationConfiguration {
#Value("${name}")
private String username;
#Value("${password}")
private String password;
#Value("${ethnicity}")
private String ethnicity;
#Bean
public Employee employee(){
Employee emp = new Employee();
ConfigParam configParam = new ConfigParam();
configParam.setEthnicity(ethnicity);
emp.setConfigParam(configParam);
return emp;
}
}
Inside the xml file
<property name="configParam">
<bean class="com.test.config.ConfigParam">
<property name="ethnicity" value="indian" />
</bean>
</property>
I am able to set the username and password properties but unable to set the configParam attribute to employee since we need to inject the ConfigParamand its a bean. Please let me know how to inject a bean inside the employee method.
Assuming that your Spring XML context that includes the configParam definition is called my-spring-context.xml and is found in a resources/spring folder, in other words in a spring folder in your classpath (name and folder are arbitrary):
Make your ApplicationConfiguration configuration class aware of that XML context file by using the #ImportResource annotation:
#Configuration
#ImportResource("classpath:spring/my-spring-context.xml")
#PropertySource("classpath:conf.properties")
public class ApplicationConfiguration {
//...
}
Then in your employee method dependency-inject the XML bean as usual. If you want to inject by name and not by type you can add the #Qualifier("myBeanName") directly prior the configParam argument definition.
#Bean
#Autowired
public Employee employee(ConfigParam configParam){
Employee emp = new Employee();
configParam.setEthnicity(ethnicity);
emp.setConfigParam(configParam);
return emp;
}

Where to store config info for spring mvc 3.x

Where is a good place to store custom config info in spring mvc 3.x and how would I access that info globally via any controller?
Is there a built in config manager?
I assume 'custom config' means a configuration file the code reads / you / your operations team can update?
One easy solution is to use spring beans xml configuration file and deploy your war in exploded fashion.
Create a configuration java class:
// File: MyConfig.java ------------------------------------
public class MyConfig {
private String supportEmail;
private String websiteName;
// getters & setters..
}
Configure the class as a Spring bean and set its properties on your spring beans xml file (can also create a new file and use <import resource="..."/>):
// File: root-context.xml ----------------------------------------
<beans ...>
...
<bean class="com.mycompany.MyConfig">
<property name="supportEmail" value="support#mycompany.com"/>
<property name="websiteName" value="Hello Site"/>
</bean>
...
</beans>
Inject your configuration class (eg: in a controller)
// File: HelloController.java ------------------------------------
#Controller
#RequestMapping("/hello")
public class HelloController {
#Autowired MyConfig config;
// ...
}
However updates to the configuration would require re-deploy / server restarts
You can also use <context:property-placeholder>.
It looks like this.
myapp.properties:
foo=bar
spring beans xml:
<context:property-placeholder location="classpath:myapp.properties"/>
Or
<context:property-placeholder location="file:///path/to/myapp.properties"/>
Controller:
import org.springframework.beans.factory.annotation.Value;
...
#Controller
public class Controller {
#Value("${foo}")
private String foo;
If you want to get properties programmatically, you can use Environment with #PropertySource.
Configuration:
#Configuration
#PropertySource("classpath:myapp.properties")
public class AppConfig {
#Bean
public PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
Controller:
#Controller
public class Controller {
#Value("${foo}")
private String foo;
#Autowired
private Environment env;
#RequestMapping(value = "dosomething")
public String doSomething() {
env.getProperty("foo");
...
}
Hope this helps.

Spring MBeanExporter - giving name to MBean

I'm trying to run a simple application with jmx-exported method. I do it like (spring-context and cglib for "#Configuration" are in classpath):
package com.sopovs.moradanen.jmx;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jmx.export.MBeanExporter;
import org.springframework.stereotype.Component;
#Component
#Configuration
public class SpringJmxTest {
public static void main(String[] args) {
new AnnotationConfigApplicationContext("com.sopovs.moradanen.jmx");
while (true) {
Thread.yield();
}
}
#Bean
public MBeanExporter createJmxExporter() {
return new MBeanExporter();
}
public interface FooBarMBean {
public String hello();
}
#Component
public static class FooBar implements FooBarMBean {
#Override
public String hello() {
return "Hello";
}
}
}
However when I run it I get:javax.management.MalformedObjectNameException: Key properties cannot be empty. I tried to debug and solved it with:
#Component
public static class FooBar implements FooBarMBean, SelfNaming {
#Override
public String hello() {
return "Hello";
}
#Override
public ObjectName getObjectName() throws MalformedObjectNameException {
return new ObjectName("fooBar:name=" + getClass().getName());
}
}
But is there a better way to supply a name for MBean?
You can use the descriptions annotations provided by Spring Context #Managed* :
To do this, you must NOT implements the interface with "MBean" or "MXBean" suffix, neither SelfNaming.
Then, the bean will be detected as a standard spring "managed bean" when MBeanExporter will registerBeanInstance(..), and will be converted to a ModelMBean using all spring annotations, including descriptions of attributes, operations, parameters, etc..
As a requirement, you should declare in your spring context the MBeanExporter with AnnotationJmxAttributeSource, MetadataNamingStrategy, and MetadataMBeanInfoAssembler attributes, which can be simplified like this (as described here):
<bean id="mbeanExporter"
class="org.springframework.jmx.export.annotation.AnnotationMBeanExporter" />
or:
<context:mbean-export />
or, using programmatic approach:
#Configuration
#EnableMBeanExport
public class AppConfig {
}
And your managed bean should look like this :
#Component("myManagedBean")
#ManagedResource(objectName="your.domain.jmx:name=MyMBean",
description="My MBean goal")
public class AnnotationTestBean {
private int age;
#ManagedAttribute(description="The age attribute", currencyTimeLimit=15)
public int getAge() {
return age;
}
#ManagedOperation(description = "Check permissions for the given activity")
#ManagedOperationParameters( {
#ManagedOperationParameter(name = "activity",
description = "The activity to check")
})
public boolean isAllowedTo(final String activity) {
// impl
}
}
Remember to not implements an MBean interface, which would be a StandardMBean, and SelfNaming interface, which would bypass Spring naming management !
You can use KeyNamingStrategy to define all JMX-related properties inside XML configuration without adding any compile-time dependencies to Spring into the source code of your MBean:
<bean class="org.springframework.jmx.export.MBeanExporter" lazy-init="false">
<property name="namingStrategy" ref="namingStrategy"/>
</bean>
<bean id="namingStrategy"
class="org.springframework.jmx.export.naming.KeyNamingStrategy">
<property name="mappings">
<props>
<prop key="someSpringBean">desired.packageName:name=desiredBeanName</prop>
</props>
</property>
</bean>
If you can live with somewhat arbitrary object names, then you can use the IdentityNamingStrategy as a naming strategy for MBeanExporter and minimize the XML configuration event further:
<bean class="org.springframework.jmx.export.MBeanExporter" lazy-init="false">
<property name="namingStrategy" ref="namingStrategy"/>
</bean>
<bean id="namingStrategy"
class="org.springframework.jmx.export.naming.IdentityNamingStrategy"/>
Check spring documentation: http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/jmx.html section 22.3.2 explains the JDK 5.0 annotations that are available.
Section 22.4 explains mechanisms available for object naming.

Resources