Main question:
Could someone advise how I use Spring to retrieve a value in my Java code to get values out of whatever properties file is specified by the context:property-placeholder tag in the applicationContext.xml?
Detailed description:
I am very new to Spring. Trying to use it to retreive configurable properties for my application to connect to a (configurable) JMS queue.
I have a Java/J2EE web application, using Spring.
In the src/main/resources/applicationContext.xml I have the following line:
<context:property-placeholder location="classpath:myapp.properties" />
Then in the src/main/resources/myapp.properties file I have the following lines:
myservice.url=tcp://someservice:4002
myservice.queue=myqueue.service.txt.v1.q
The problem that I am having, is that, for the life of me, I cannot figure out how to get the value of myservice.url that is defined in myapp.properties into my running java code.
I have tried a static function [to be called around the application]:
public static String getProperty(String propName)
{
WebApplicationContext ctx =
FacesContextUtils.getWebApplicationContext(FacesContext.getCurrentInstance());
Environment env = ctx.getEnvironment();
retVal = env.getProperty(propName);
return retVal;
}
However, while it returns a populated Environment object "env", the env.getProperty(propName) method returns null.
Ok - many, many thanks to Rustam who gave me the clues I needed. The way that I resolved this was thus:
In the src/main/resources/applicationContext.xml I have the following line:
<context:property-placeholder location="classpath:myapp.properties" />
Then in the src/main/resources/myapp.properties file I have the following lines:
myservice.url=tcp://someservice:4002
myservice.queue=myqueue.service.txt.v1.q
Then I have a class as follows:
package my.app.util;
import org.springframework.beans.factory.annotation.Value;
public class ConfigInformation
{
public ConfigInformation()
{
// Empty constructor needed to instantiate beans
}
#Value("${myservice.url}")
private String _myServiceUrl;
public String getMyServiceUrl()
{
return _myServiceUrl;
}
#Value("${myservice.queue}")
private String _myServiceQueue;
public String getMyServiceQueue()
{
return _myServiceQueue;
}
}
Then, I have the following declaration in my applicationContext.xml:
<bean name="configInformation" class="my.app.util.ConfigInformation">
</bean>
After I have done this, I am able to use the following lines in my code:
WebApplicationContext ctx = FacesContextUtils.getWebApplicationContext(FacesContext.getCurrentInstance());
ConfigInformation configInfo = (ConfigInformation) ctx.getBean("configInformation");
And consequently, the configInfo object, is populated with the values assigned to "myservice.url" and "myservice.queue" which can be retrieved with the method calls:
configInfo.getMyServiceUrl()
and
configInfo.getMyServiceQueue()
Related
I'd like to accomplish this: Environment Specific application.properties file in Spring Boot application
in a Spring non-Boot application. Any idea on how to do that? Now I am setting environment variables to tell the application which properties to use, would prefer to do it the "boot" way.
Help would be appreciated.
In order to represent the several environments use profiles. If you want to know more browse this site. and I think this is exactly what you are looking for.
Update 1:
Considering you have a fixed suffix of your property files and you have a set of property files for different environment, for example,
development-it_wroks.properties,
test-it_wroks.properties etc. etc.
etc.it_wroks
is the suffix
Determine the active enviourment from active_env.properties
profiles.active: development
#profiles.active: test
#profiles.active: stage
#profiles.active: production
Write a custom Property resolver
import org.apache.commons.configuration2.Configuration;
import org.apache.commons.configuration2.FileBasedConfiguration;
import org.apache.commons.configuration2.PropertiesConfiguration;
import org.apache.commons.configuration2.builder.FileBasedConfigurationBuilder;
import org.apache.commons.configuration2.builder.fluent.Parameters;
public class MyPropertyUtil {
public static String getValuesFromPerpertyFile(String filename,String key){
String value = null;
Configuration config = getConfiguration(filename);
value = config.getString(key);
return value;
}
public static Configuration getConfiguration(String file){
Configuration config = null;
try{
Parameters params = new Parameters();
FileBasedConfigurationBuilder<FileBasedConfiguration>
builder =new FileBasedConfigurationBuilder
<FileBasedConfiguration>(PropertiesConfiguration.class)
.configure(params.properties().setFileName(file));
config = builder.getConfiguration();
}catch(Exception ex){
ex.printStackTrace();
}finally{
}
return config;
}
}
Now your calling class
import org.apache.log4j.Logger;
public class MyCallingClass {
final static Logger logger = Logger.getLogger(this.getClass());
//Determine the active enviourment,You may determine this from os environment variable if you want
String activeEnvironment = MyPropertyUtil.
getValuesFromPerpertyFile("resource/active_env.properties"
,"profiles.active");
//Set the property file
String myEnvSpecificValue = MyPropertyUtil.
getValuesFromPerpertyFile("resource/"+activeEnvironment+"it_wroks.properties",
"my.property.string");
//Do what you want to
logger.info(myEnvSpecificValue);
}
You can add application-environment.properties as per environment. Spring boot should automatically detect the corresponding properties file based on active environment.
Let say I have the follow code.
private static String configFile = null;
File cf = new File(configFile);
Configuration c = new Configuration();
if (cf.exists() && cf.isFile()) {
c.configure(cf);
} else {
c.configure(configFile);
}
I am wondering what is the difference between c.configure(cf) and c.configure(configFile). In my code,configFile is repsented as resource and cf is the the configFile object.
I found these two from this (api).
public Configuration configure(String resource)
throws HibernateException
public Configuration configure(File configFile)
throws HibernateException
The documentation of the API isn't explicitly clear, is it?
I tracked it as far as this class before getting fed up:
https://github.com/hibernate/hibernate-orm/blob/master/hibernate-core/src/main/java/org/hibernate/boot/cfgxml/internal/ConfigLoader.java
But it looks like in case of configure(String resource), it is the name of a resource as would be passed to the Java class loader to get a resource as a stream, i.e.:
http://docs.oracle.com/javase/7/docs/api/java/lang/Class.html#getResourceAsStream(java.lang.String)
Whereas, configure(File configFile), it uses a FileInputStream.
In either case Hibernate is still expecting the same XML format for the configuration.
I'm trying to read deployment specific information from a properties file in my wildfly configuration folder. I tried this:
#Singleton
#Startup
public class DeploymentConfiguration {
protected Properties props;
#PostConstruct
public void readConfig() {
props = new Properties();
try {
props.load(getClass().getClassLoader().getResourceAsStream("my.properties"));
} catch (IOException e) {
// ... whatever
}
}
But apparently this is not working since the configuration folder is not in the classpath anymore. Now I can't find an easy way to do it. My favorite would be something like this:
#InjectProperties("my.properties")
protected Properties props;
The only solution I found on the web so far involves making my own OSGi module, but I believe there must be an easier way to do it (one without OSGi!). Can anyone show me how?
If you want to explicitly read a file from the configuration directory (e.g. $WILDFLY_HOME/standalone/configuration or domain/configuration) there's a system property with the path in it. Simply do System.getProperty("jboss.server.config.dir"); and append your file name to that to get the file.
You wouldn't read it as a resource though, so...
String fileName = System.getProperty("jboss.server.config.dir") + "/my.properties";
try(FileInputStream fis = new FileInputStream(fileName)) {
properties.load(fis);
}
Then the file would be loaded for you.
Also, since WildFly doesn't ship with OSGi support anymore, I don't know how creating an OSGi module would help you here.
Here is a full example using just CDI, taken from this site.
Create and populate a properties file inside the WildFly configuration folder
$ echo 'docs.dir=/var/documents' >> .standalone/configuration/application.properties
Add a system property to the WildFly configuration file.
$ ./bin/jboss-cli.sh --connect
[standalone#localhost:9990 /] /system-property=application.properties:add(value=${jboss.server.config.dir}/application.properties)
This will add the following to your server configuration file (standalone.xml or domain.xml):
<system-properties>
<property name="application.properties" value="${jboss.server.config.dir}/application.properties"/>
</system-properties>
Create the singleton session bean that loads and stores the application wide properties
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import javax.annotation.PostConstruct;
import javax.ejb.Singleton;
#Singleton
public class PropertyFileResolver {
private Logger logger = Logger.getLogger(PropertyFileResolver.class);
private String properties = new HashMap<>();
#PostConstruct
private void init() throws IOException {
//matches the property name as defined in the system-properties element in WildFly
String propertyFile = System.getProperty("application.properties");
File file = new File(propertyFile);
Properties properties = new Properties();
try {
properties.load(new FileInputStream(file));
} catch (IOException e) {
logger.error("Unable to load properties file", e);
}
HashMap hashMap = new HashMap<>(properties);
this.properties.putAll(hashMap);
}
public String getProperty(String key) {
return properties.get(key);
}
}
Create the CDI Qualifier. We will use this annotation on the Java variables we wish to inject into.
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.inject.Qualifier;
#Qualifier
#Retention(RetentionPolicy.RUNTIME)
#Target({ ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR })
public #interface ApplicationProperty {
// no default meaning a value is mandatory
#Nonbinding
String name();
}
Create the producer method; this generates the object to be injected
import javax.enterprise.inject.Produces;
import javax.enterprise.inject.spi.InjectionPoint;
import javax.inject.Inject;
public class ApplicaitonPropertyProducer {
#Inject
private PropertyFileResolver fileResolver;
#Produces
#ApplicationProperty(name = "")
public String getPropertyAsString(InjectionPoint injectionPoint) {
String propertyName = injectionPoint.getAnnotated().getAnnotation(ApplicationProperty.class).name();
String value = fileResolver.getProperty(propertyName);
if (value == null || propertyName.trim().length() == 0) {
throw new IllegalArgumentException("No property found with name " + value);
}
return value;
}
#Produces
#ApplicationProperty(name="")
public Integer getPropertyAsInteger(InjectionPoint injectionPoint) {
String value = getPropertyAsString(injectionPoint);
return value == null ? null : Integer.valueOf(value);
}
}
Lastly inject the property into one of your CDI beans
import javax.ejb.Stateless;
import javax.inject.Inject;
#Stateless
public class MySimpleEJB {
#Inject
#ApplicationProperty(name = "docs.dir")
private String myProperty;
public String getProperty() {
return myProperty;
}
}
The simplest thing you can do is to run standalone.sh with a -P option referencing your properties file (you need a URL file:/path/to/my.properties, or put the file in $WILDFLY_HOME/bin).
Then all properties from the file will be loaded as system properties.
For injecting configuration properties into your application classes, have a look at DeltaSpike Configuration, which supports different property sources like system properties, environment variables, JNDI entries and hides the specific source from your application.
Alternatively, to avoid setting system properties (which will be global in the sense of being visible to all applications deployed to your WildFly instance), you can also define a custom property source for DeltaSpike reading a properties file from any given location, and these properties will be local to your application.
It sounds like the problem you are trying to solve is managing different (but probably similar) configuration files for running your application in different environments (ie, Production, QA, or even different customers). If that is the case, take a look at Jfig http://jfig.sourceforge.net/ . It would obviate the need for storing property files outside your classpath (but you still could).
What is needed is a hierarchical approach to configuration files. The ninety percent of configuration values that do not change can be maintained in a base file. The other ten percent (or less) may be maintained in their own distinct configuration file. At run time, the files are layered on top of each other to provide a flexible, manageable configuration. For example, in a development environment myhost.config.xml combines with dev.config.xml and base.config.xml to form my unique configuration.
Each configuration file may then be maintained in version control as they have unique names. Only the base files need to be modified when base values change, and it is easy to see the difference between versions. Another major benefit is that changes to the base configuration file will be exhaustively tested before deployment.
InputStream in = null;
File confDir = new File(System.getProperty("jboss.server.config.dir"));
File fileProp = new File(confDir, "my.properties");
try{
//teste fileProp.exists etc.
in = new FileInputStream(fileProp);
Properties properties = new Properties();
properties.load(in);
//You should throws or handle FileNotFoundException and IOException
}finally{
try{
in.close();
}catch(Exception ignored){
}
}
To avoid this kind of problem the issue is to set the jboss.server.config.dir in VM arguments like that :
-Djboss.server.config.dir="[jboss_repository]/server/[default-all-standard-standalone]/conf" –server
If you have in standalone.xml property:
<property name="my.properties" value="propertyValue"/>
you can wasily read it with:
static final String MY_PROPERTY = System.getProperty("my.properties");
Or if you specify context param in web.xml like:
<context-param>
<param-name>MyProperty</param-name>
<param-value>MyPropertyValue</param-value>
</context-param>
You can read it in Java bean:
String myProperty= getServletContext().getInitParameter("MyProperty");
where i have to read and write userdata.properties from UI, every thing up to now is working like reading and writing ,before i mention the hardcoded direct path in file like this
File f = new File("D:\\user\\userdata.properties")
but my problem is if i mention like this i cannot change the path,for that i have to mention
D:\user\userdata.properties in another path.properties file ,now i have to read path.properties file in
File f = new File(........)
please help me how o do that.this is how presently i am using to read userdata.properties
#RequestMapping("/proxy")
public String ProxySettings(Model model) throws Exception {
File f = new File("D:\\sahi\\userdata.properties");
//String path = MobeeProxyChangeController.class.getResourceAsStream("/property/path.properties").toString();
Properties properties = new Properties();
try {
properties.load(new FileInputStream(f));
String getHost = properties.getProperty("ext.http.proxy.host");
String getPort = properties.getProperty("ext.http.proxy.port");
model.addAttribute("proxyHost", getHost.trim());
model.addAttribute("proxyPort", getPort.trim());
} catch (Exception e) {
e.printStackTrace();
}
return "proxyFile";
}
Thanks in advance
venu
First of all, you shouldn't call the load method for each request. That will turn out very expensive for you.
Secondly, since you're already using spring, there is no need to read a properties file this way. You can just inject it.
I suggest creating fields for proxyHost and proxyPort in your class as fields.
public class MyController {
...
private String proxyHost;
private String proxyPort;
#RequestMapping("/proxy")
public String ProxySettings(Model model) throws Exception {
model.addAttribute("proxyHost", this.proxyHost);
model.addAttribute("proxyPort", this.proxyPort);
return "proxyFile";
}
// provide setters for the above fields or use #Autowired
}
Your spring configuration would look like:
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:userdate.properties" />
</bean>
<bean class="com.foo.MyController">
<property name="proxyHost" value="${ext.http.proxy.host}"/>
<property name="proxyPort" value="${ext.http.proxy.port}"/>
</bean>
You can read the userdata.properties either from the classpath or file system depending on where you keep it.
Put your properties file on the classpath and reference it via a MessageSource
Example:
Properties file "foo.properties":
foo=bar
baz=phleem
Java code:
public static void main(final String[] args) {
final ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasename("foo");
System.out.println(messageSource.getMessage("foo", new Object[0], Locale.getDefault()));
}
Output:
bar
You can let Spring inject that MessageSource, but I think you have to do that through XML (or through #Configuration). But you can also do it manually, however, you should only do it once per class, not once per request.
OK, here's an example of how you could wire it together using Spring:
XML configuration:
<bean id="proxySettings" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="userdata" />
</bean>
Controller class:
#Controller
public class ProxyController {
private static final Locale LOCALE = Locale.getDefault();
private static final Object[] EMPTY_ARGS = new Object[0];
#Autowired #Qualifier("proxySettings")
private MessageSource messageSource;
#RequestMapping("/proxy")
public String proxySettings(final Model model) throws Exception {
model.addAttribute("proxyHost", messageSource.getMessage("ext.http.proxy.host", EMPTY_ARGS, LOCALE));
model.addAttribute("proxyPort", messageSource.getMessage("ext.http.proxy.port", EMPTY_ARGS, LOCALE));
return "proxyFile";
}
}
all, I am puzzled about the struts2 action unit test
import org.apache.struts2.StrutsSpringTestCase;
import org.junit.Test;
import com.opensymphony.xwork2.ActionProxy;
public class TestLoginAction extends StrutsSpringTestCase {
#Test
public void testCheck() throws Exception {
ActionProxy proxy = null;
LoginAction test = null;
request.setParameter("username", "admin");
proxy = getActionProxy("/checkLogin");
test = (LoginAction) proxy.getAction();
String result = proxy.execute();
assertEquals("error", result);
assertEquals("admin", test.getUsername());
}
}
It throw the warnings and exceptions:
Couldn't get resource paths for class path resource [WEB-INF/jsp/]
java.io.FileNotFoundException: class path resource [WEB-INF/jsp/] cannot be resolved to URL because it does not exist
StrutsSpringTestCase is expecting the Spring configuration to be loaded from classpath:applicationContext.xml. You can override this behavior by adding this to your test action:
#Override
public String getContextLocations() {
return "path/to/your/TestLoginAction-context.xml";
}
the reason of throwing the exception has been found,
I use struts-convention to find my action classes,but
,this configuration is the base search path of jsp files,so of course the convention can't recognize the path as java class path,I will provide the two workaround here:
you can modify the configuration value "/WEB-INF/jsp" to the existing class path,such as "com.foo.bar" to make the convention resolve class path smoothly
rewrite the MockServletContext and swallow the throwing Exception
public Set getResourcePaths(String path) {
....
}
You may add a line as follows to struts.xml to work around it.
<constant name="struts.convention.result.path" value="/your/path" />