I'm using org.springframework.beans.factory.config.PropertyPlaceholderConfigurer for reading properties and loading them.
I want print all the properties loaded by them,please help?
If you're looking to print out the properties that the PropertyPlaceholderConfigurer has loaded and will use, then you may do best to subclass PropertyPlaceholderConfigurer and log out the properties yourself. Its easy enough - for example:
public class LoggingPropertyPlaceholderConfigurer extends PropertyPlaceholderConfigurer {
private static final Logger LOG = Logger.getLogger(LoggingPropertyPlaceholderConfigurer.class);
#Override
protected Properties mergeProperties() throws IOException {
Properties props = super.mergeProperties();
for (String name : props.stringPropertyNames()) {
LOG.debug(name + ": " + props.getProperty(name));
}
return props;
}
}
And then update your Spring config:
<bean class="LoggingPropertyPlaceholderConfigurer">
<property name="location" value="classpath:myprops.properties"/>
</bean>
if you are using log4j, you can log it by adding:
<logger name="org.springframework.beans.factory.config">
<level value="info" />
</logger>
It will show:
INFO : org.springframework.beans.factory.config.PropertyPlaceholderConfigurer - Loading properties file from class path resource [myApp.properties]
Related
I want to be able to read the active profiles from a property file so that different environments (dev,prod etc) can be configured with different profiles in a Spring MVC based web application. I know that the active profiles can be set through JVM params or system properties. But I would like to do it through a property file instead. The point is that I dont know the active profile statically and instead want to read it from a properties file. It looks like this is not possible. For eg., if I had 'spring.profiles.active=dev' in application.properties, and allow it to be overridden in override.properties like so:
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="ignoreResourceNotFound" value="true" />
<property name="locations">
<list>
<value>classpath:/application.properties</value>
<value>file:/overrides.properties</value>
</list>
</property>
</bean>
the profile is not being picked up in the environment. I guess this is because the active profiles are being checked before bean initialization, and therefore do not honor the property being set in a properties file. The only other option I see is to implement an ApplicationContextInitializer that will load those property files in order of priority(override.properties first if it exists, else application.properties) and set the value in context.getEnvironment(). Is there a better way to do it from properties files?
One solution to do it is to read necessary property file with specified profile "manually" - without spring - and set profile at context initialization:
1) Write simple properties loader:
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
import java.util.Properties;
public class PropertiesLoader
{
private static Properties props;
public static String getActiveProfile()
{
if (props == null)
{
props = initProperties();
}
return props.getProperty("profile");
}
private static Properties initProperties()
{
String propertiesFile = "app.properties";
try (Reader in = new FileReader(propertiesFile))
{
props = new Properties();
props.load(in);
}
catch (IOException e)
{
System.out.println("Error while reading properties file: " + e.getMessage());
return null;
}
return props;
}
}
2) Read profile from properties file and set it during Spring container initialization (example with Java-based configuration):
public static void main(String[] args)
{
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.getEnvironment().setActiveProfiles(PropertiesLoader.getActiveProfile());
ctx.register(AppConfig.class);
ctx.refresh();
// you application is running ...
ctx.close();
}
Using Apache Commons Configurations 1.9, how to avoid ConfigurationException upon loading a configuration file if the provided file cannot be found?
The Spring app context resembles:
<bean name="foo.config" class="org.apache.commons.configuration.PropertiesConfiguration" init-method="load">
<property name="fileName" value="foo.properties" />
</bean>
However my config file is optional, so I want to make sure the application starts correctly even the file doesn't exist.
How can I achieve this with Commons Configurations? A FactoryBean works, but is there another way?
if (!file.exists()) return new PropertiesConfiguration();
Or using try/catch syntax using an XML configuration:
import org.apache.commons.configuration2.XMLConfiguration;
import org.apache.commons.configuration2.builder.fluent.Configurations;
public class Workspace {
private final XMLConfiguration mConfig;
public Workspace() {
final var configs = new Configurations();
XMLConfiguration config;
try {
config = configs.xml( "filename.xml" );
} catch( final Exception e ) {
config = new XMLConfiguration();
}
mConfig = config;
}
Using a regular properties configuration will work the same way.
I have two classes ClientLogic1 and WelcomeBean1 as follows
public class ClientLogic1 {
public static void main(String[] args)
{
Resource res = new ClassPathResource("spconfig.xml");
BeanFactory factory = new XmlBeanFactory(res);
Object o = factory.getBean("id1");
WelcomeBean1 wb = (WelcomeBean1)o;
wb.show();
}
}
2nd class
public class WelcomeBean1 {
private Map data;
public void setData(Map data) {
this.data = data;
}
public void show()
{
Set s=data.entrySet();
Iterator it = s.iterator();
while(it.hasNext())
{
Map.Entry me = (Map.Entry)it.next();
System.out.println(me.getKey()+ " - "+me.getValue());
}
}
}
I have a xml file as
<beans>
<bean id="id1" class="WelcomeBean1">
<property name="data">
<map>
<entry key="k1">
<vlaue>1323</value>
</entry>
<entry key="k2">
<value>feed</value>
</entry>
</map>
</property>
</bean>
</bean>
I have given the right path.It's just when i run this program i get the following error
Exception in thread "main" java.lang.ClassCastException: WelcomeBean cannot be
cast to mapexmpl.WelcomeBean1 at mapexmpl.ClientLogic1.main(ClientLogic1.java:15)
I am not sure where i am going wrong.Can someone help me plz...
make sure there is no duplicate bean id in spring configuration file. for instance you might have bean WelcomeBean with id id1
change to full package name <bean id="id1" class="mapexmpl.WelcomeBean1">
Actually it is saying as WelcomeBean cannot be ..............But your code is showing all as WelcomeBean1.
You used WelcomeBean some where .Please check it once.
I think before you used WelComeBean.Then changed it to WelComeBean1.Please build agian with clean.
It may be a duplicate question.
My question is: I want to read a property from properties file and put that in servlet context as soon as I start my application.
Could any one please help me on that?
Thanks in advance.
Implement Spring's ApplicationListener:
#Component
public class MyApplicationListener implements ApplicationListener {
/* if you want to set predefined properties you even don't have to load properties filed - you can directly inject properties values ... you can configure it in applicationContext.xml
<util:list id="locations">
<value>classpath:appconfig1.properties</value>
<value>classpath:appconfig2.properties</value>
</util:list>
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
p:locations-ref="locations" />
*/
#Value("${myproperty1}") private String myProperty1;
#Value("${myproperty2}") private String myProperty2;
#Value("${myproperty3}") private String myProperty3;
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ContextClosedEvent) {
applicationClosed();
return;
}
if (!(event instanceof ContextRefreshedEvent)) return;
ContextRefreshedEvent e = (ContextRefreshedEvent) event;
ApplicationContext appContext = e.getApplicationContext();
if (!(appContext instanceof WebApplicationContext)) return;
WebApplicationContext ctx = (WebApplicationContext) e.getApplicationContext();
ServletContext context = ctx.getServletContext();
context.setAttribute("myProperty1", myProperty1);
context.setAttribute("myProperty2", myProperty2);
context.setAttribute("myProperty3", myProperty3);
}
}
Is there anything that can achieve the equivalent of the below:
<import resource="a.xml">
<prop name="key" value="a"/>
</import>
<import resource="a.xml">
<prop name="key" value="b"/>
</import>
Such that the beans defined in resouce a would see the property key with two different values? The intention would be that this would be used to name the beans in the imports such that resource a.xml would appear:
<bean id="${key}"/>
And hence the application would have two beans named a and b now available with the same definition but as distinct instances. I know about prototype scope; it is not intended for this reason, there will be many objects created with interdepednencies that are not actually prototypes. Currently I am simply copying a.xml, creating b.xml and renaming all the beans using the equivalent of a sed command. I feel there must be a better way.
I suppose that PropertyPlaceholderConfigurers work on a per container basis, so you can't achieve this with xml imports.
Re The application would have two beans named a and b now available with the same definition but as distinct instances
I think you should consider creating additional application contexts(ClassPathXmlApplicationContext for example) manually, using your current application context as the parent application context.
So your many objects created with interdependencies sets will reside in its own container each.
However, in this case you will not be able to reference b-beans from a-container.
update you can postprocess the bean definitions(add new ones) manually by registering a BeanDefinitionRegistryPostProcessor specialized bean, but this solution also does not seem to be easy.
OK, here's my rough attempt to import xml file manually:
disclaimer: I'm very bad java io programmer actually so double check the resource related code :-)
public class CustomXmlImporter implements BeanDefinitionRegistryPostProcessor {
#Override
public void postProcessBeanFactory(
ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
private Map<String, String> properties;
public void setProperties(Map<String, String> properties) {
this.properties = properties;
}
public Map<String, String> getProperties() {
return properties;
}
private void readXml(XmlBeanDefinitionReader reader) {
InputStream inputStream;
try {
inputStream = new ClassPathResource(this.classpathXmlLocation).getInputStream();
} catch (IOException e1) {
throw new AssertionError();
}
try {
Scanner sc = new Scanner(inputStream);
try {
sc.useDelimiter("\\A");
if (!sc.hasNext())
throw new AssertionError();
String entireXml = sc.next();
PropertyPlaceholderHelper helper = new PropertyPlaceholderHelper("${",
"}", null, false);
Properties props = new Properties();
props.putAll(this.properties);
String newXml = helper.replacePlaceholders(entireXml, props);
reader.loadBeanDefinitions(new ByteArrayResource(newXml.getBytes()));
} finally {
sc.close();
}
} finally {
try {
inputStream.close();
} catch (IOException e) {
throw new AssertionError();
}
}
}
private String classpathXmlLocation;
public void setClassPathXmlLocation(String classpathXmlLocation) {
this.classpathXmlLocation = classpathXmlLocation;
}
public String getClassPathXmlLocation() {
return this.classpathXmlLocation;
}
#Override
public void postProcessBeanDefinitionRegistry(
BeanDefinitionRegistry registry) throws BeansException {
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(registry);
readXml(reader);
}
}
XML configuration:
<bean class="CustomXmlImporter">
<property name="classPathXmlLocation" value="a.xml" />
<property name="properties">
<map>
<entry key="key" value="a" />
</map>
</property>
</bean>
<bean class="CustomXmlImporter">
<property name="classPathXmlLocation" value="a.xml" />
<property name="properties">
<map>
<entry key="key" value="b" />
</map>
</property>
</bean>
this code loads the resources from classpath. I would think twice before doing something like that, anyway, you can use this as a starting point.