Spring configure application.properties for placeholder in multitenant environment - java

I have a multi tenant environment so I need to change some path from application.properties in runtime to use the folder of specific tenant.
For example in my application properties:
image.avatars=C:/Users/Public/Pictures/Sample Pictures/${tenant}/Avatars/
in my class I use
#Autowired
private Environment env;
private static final String DIRECTORY_USER_IMAGE = "image.avatars";
.....Method
env.getRequiredProperty(DIRECTORY_USER_IMAGE)
I read about env.resolveRequiredPlaceholders but I don't understand how it can be used in my case since it has only one parameter like so env.resolveRequiredPlaceholders(TenantContext.getCurrentTenant()).
Is there a simple way to change the placeholder without manipulate String(with replace)?
I thought that env.resolveRequiredPlaceholders required the name of properties and the varargs of placeholder but it is different.
Thanks

You can use String.format().
Just use %s in properties
image.avatars=C:/Users/Public/Pictures/Sample Pictures/%s/Avatars/
And the in the code
String.format(imageavatars, tenant)

This might be not exactly what you want (because I struggle to understand your scenario), but what about putting
image.avatars=C:/Users/Public/Pictures/Sample Pictures/${tenant}/Avatars/
in your application.properties, and using
#Value("${image.avatars}")
private String DIRECTORY_USER_IMAGE;
in your bean/service and running the app with a command line argument like
--tenant="FooBar"
This would give DIRECTORY_USER_IMAGE the value C:/Users/Public/Pictures/Sample Pictures/FooBar/Avatars/ and you could change the CLI argument according to your needs. But be aware that DIRECTORY_USER_IMAGE is not static final anymore.
I hope I got your requirements right.

Related

Passing multiple #Value into a #Bean in a java class

I am trying to write a Spring Batch job that has two steps in it. They are both the same step but with different file locations. As such I need to pass multiple strings into the job bean to let the step know where to send the different files. However, it I try to pass the Resource values, I get a NoSuchBeanDefinitionException. The answers I found to this is that I need to add a #Value to the bean to tell it that the beans needs to a parameter to work.
But that is for only one value.
Is there a way to pass multiple #Values to a bean using java configuration? Below is the code I am using.
#Value("#{'${batch.outputFile}'}")
Resource outputFilePath;
#Value("#{'${batch.outputFileTrg}'}")
Resource outputFilePathTrg;
#Bean
public Step toServerStep(Resource outputFile) {
return stepBuilderFactory.get("toServerStep")
.chunk(1)
.reader(xmlFileItemReader())
.writer((ItemWriter<? super Object>) flatFileItemWriter(outputFile))
.build();
}
#Bean
public Job fileToServerJob(JobBuilderFactory jobBuilderFactory){
return jobBuilderFactory.get("fileToServerJob")
.start(toServerStep(outputFilePath1))
.next(toServerStep(outputFilePath2))
.build();
}
You can pass a delimited String as the property and break it apart into a list for your Value object.
#Value("#{'${batch.outputFiles}'.split(',')}")
private List<String> outputFilePaths;
With your application.property with the following
batch.outputFiles=/tmp/a,/tmp/b,/tmp/c
You can then use these path strings to grab the appropriate Resource to be used by your writer.
You are putting in a string, but shouldn't you put in a Rescource? Got one not so nice example that once worked for me here. Maby it can help you.

Setting logger properties programatically to show hibernate queries

I'm using Hibernate and java.util.Logger class for logs in my project. I have a separate config file for both. I am able to switch between showing and not showing SQL queries by setting org.hibernate.SQL.level property to ALL in the log configuration file, but I can't figure out how to do it programatically (I want to handle this through run parameters but without having to use two seperate log configuration files).
So far I have tried setting this parameter in hibernate Configuration class, to no avail (properties are getting set, I double checked, but no queries show up).
Then I figured it must be handled by Logger class itself, but LogManager does not have any methods for setting a property. Browsing through the web guided me towards FileHandler class but I am able to set only the 'usual' log properties (like pattern, level, etc).
Does it mean I'm wrong in thinking I have to change the Logger class and it should in fact be set in hibernate's Configuration? If that's the case, why did it not work?
...I can't figure out how to do it programmatically (I want to handle this through run parameters but without having to use two separate log configuration files).
I would have assumed that the current situation solved the problem as log configurations are set using a run-time parameter. That said, the general approach is to get and store a strong reference to the logger and change the properties of that logger.
private static final Logger hardRef = Logger.getLogger("org.hibernate.SQL");
static {
if (traceSql()) {
hardRef.setLevel(Level.ALL);
}
}
private static boolean traceSql() {
return true; //#todo Add code.
}

#Value for Injected Application Properties

I have the following in my application.properties;
spring.datasource.username=${USERNAME}
spring.datasource.password=${PASSWORD}
spring.datasource.url=${DB_URL}
...
twitter.consumer.key=${CONSUMER_KEY}
twitter.consumer.secret=${CONSUMER_SECRET}
twitter.access.key=${ACCESS_KEY}
twitter.access.secret=${ACCESS_SECRET}
The top three properties are set by the environment when the application is pushed up to a heroku instance. The values that get set by the environment are then used to make connections to the database specific to that environment. This is all good.
The problem is with the last four. As these are sensitive information I need them to be injected by the environment also. I have then used autowiring to add these to a spring boot component.
#Value("${twitter.consumer.key}")
private String consumerKey;
#Value("${twitter.consumer.secret}")
private String consumerSecret;
#Value("${twitter.access.key}")
private String accessKey;
#Value("${twitter.access.secret}")
private String accessSecret;
Now, when I build the application it complains of being unable to resolve the ACCESS_KEY etc because I do not have these environment variables on my local machine.
Could not resolve placeholder 'CONSUMER_KEY' in string value "${CONSUMER_KEY}"
How can I build the application without explicitly setting these values? Is there, for example, some way of setting defaults for the application properties if they cannot be resolved?
With the #Value annotiation it is possible to set a default value:
#Value("${my.property:default}")
To prevent the build from failing you can specify default values for the application properties if the key could not be resolved. For example;
twitter.consumer.key=${CONSUMER_KEY:fallback}
twitter.consumer.secret=${CONSUMER_SECRET:fallback}
twitter.access.key=${ACCESS_KEY:fallback}
twitter.access.secret=${ACCESS_SECRET:fallback}

REQ: Retrieving properties in my java app. collected by "PropertyPlaceholderConfigurer" -- properties are stored in a database as key/value

PUsing Spring 3.2.0.release and JDK 1.6. I've a standalone Java program (NOT running inside tomcat etal) and I'm loading properties from a database.
I've used this excellent article as a base and it works perfectly. Using the PropertiesPrinter bean (defined there) as a base and adding getters I can do stuff like getFileLocation(), getPetDogsName() but then I need to have/create setter/getters for every property.
What I would like to have is a Spring Bean or normal Java class called DatabaseProperties with a method like getProperty("filelocation"); which I can use in my application (main)and so I can retrieve/get the value of the property filelocation which is somewhere inside the information collected by PropertyPlaceholderConfigurer.
I've done a lot of digging but can't seem to find the information I need or at least I'm not able to combine the gathered info into a working program as I'm not fluent with Spring....
Any hint/pointers/urls/code is higly appreciated. It's probably relative easy but it is still out of reach for me atm.
One solution for reading values set by the PropertyPlaceholderConfigurer, is to use the #Value annotation rather than a method for setting class member variables:
class MyClass {
#Value("${file.location}")
private String fileLocation;
...
}

Is there any syntax like : #{systemProperties['environment_variable_name']} to get the system variable?

Does using #{systemProperties['environment']} in the applicationcontext.xml file of Spring return the value associated with environment?
Or is there any way to ge the system variable value in the spring applicationcontext.xml file.
When I remember right, then there is a difference between:
You can access the system properties in different ways:
#{systemProperties['databaseName']}
#{systemProperties.databaseName}
${databaseName} //$ instead of # !!
With #{systemProperties['databaseName']} you have access to system-system-properties.
With #{systemProperties.databaseName} you have access to the system properties readed for example from the command line (-DdatabaseName="testDB").
With ${databaseName} you have access the the properties from the properties files loaded and provided for example by the PropertyPlaceholderConfigurer and to the system prooperties too
#Value("#{systemProperties['java.version']}")
private String javaVersionMap;
//Dont know how
//#Value("#{systemProperties.javav.version}")
//private String javaVersionDirect;
#Value("${java.version}")
private String javaVersionProp;
//-DcmdParam=helloWorld
#Value("#{systemProperties['cmdParam']}")
private String cmdParamMap;
#Value("#{systemProperties.cmdParam}")
private String cmdParamDirect;
#Value("${cmdParam}")
private String cmdParamProp
You can use all of them in a #Value annotation or the config.xml files (<property name="databaseName" value="#{systemProperties.databaseName}"/>)
One way to do this kind of thing is to use a PropertyPlaceholderConfigurer which can be configured to use the system properties.
I also noticed that the Spring 3.1 M1 blog entry talks about new stuff for accessing configuration information from "the environment". Of course, that is only a milestone ... not a production-ready release.

Categories

Resources