I have a bundle which uses a configuration file org.jemz.karaf.tutorial.hello.service.config.cfg with one property:
org.jemz.karaf.tutorial.hello.service.msg="I am a HelloServiceConfig!!"
My blueprint for using ConfigAdmin is like:
<cm:property-placeholder persistent-id="org.jemz.karaf.tutorial.hello.service.config" update-strategy="reload" >
<cm:default-properties>
<cm:property name="org.jemz.karaf.tutorial.hello.service.msg" value="Hello World!"/>
</cm:default-properties>
</cm:property-placeholder>
<bean id="hello-service-config"
class="org.jemz.karaf.tutorial.hello.service.config.internal.HelloServiceConfig"
init-method="startup"
destroy-method="shutdown">
<property name="helloServiceConfiguration">
<props>
<prop key="org.jemz.karaf.tutorial.hello.service.msg" value="${org.jemz.karaf.tutorial.hello.service.msg}"/>
</props>
</property>
</bean>
<service ref="hello-service-config" interface="org.jemz.karaf.tutorial.hello.service.IHelloService" />
This works fine as long as I can change the value of the property and the bundle automatically updates the property.
I am wondering if there's any way of adding a new property to my config file without having to change the blueprint (which involves compile/package again).Of course my bundle should be ready to handle new properties.
Not sure if this makes sense in OSGi. Can anyone give me a hint of how to dynamically add new properties to an existing configuration file and make them available in ConfigAdmin?
You can add a property in karaf shell :
1/ config:edit org.jemz.karaf.tutorial.hello.service.config
2/ config:propset <key> <value>
3/ finally config:update to save change to file.
If you want to do it programatically, check karaf source to see the implementation
#yodamad showed me that properties were being updated in my ConfigurationAdmin service, but unfortunately my bundel was not receiving the new properties because in the bean definition I was just using a fixed property.
Finally in order to get new properties from config files I have changed my blueprint definition as follows:
<cm:property-placeholder persistent-id="org.jemz.karaf.tutorial.hello.service.config" update-strategy="reload" >
</cm:property-placeholder>
<bean id="hello-service-config"
class="org.jemz.karaf.tutorial.hello.service.config.internal.HelloServiceConfig"
init-method="startup"
destroy-method="shutdown">
</bean>
<service ref="hello-service-config" interface="org.jemz.karaf.tutorial.hello.service.IHelloService" />
<reference id="hello-service-config-admin" interface="org.osgi.service.cm.ConfigurationAdmin"
availability="optional">
<reference-listener bind-method="setConfigAdmin"
unbind-method="unsetConfigAdmin">
<ref component-id="hello-service-config"/>
</reference-listener>
</reference>
I have a reference to the ConfigurationAdmin service so, now I can check all properties for my bundle as:
public void setConfigAdmin(ConfigurationAdmin cfgAdmin) {
this.cfgAdmin = cfgAdmin;
try {
Configuration cfg = cfgAdmin.getConfiguration("org.jemz.karaf.tutorial.hello.service.config");
Dictionary<String, Object> properties = cfg.getProperties();
Enumeration<String> en = properties.keys();
while(en.hasMoreElements()) {
String key = en.nextElement();
System.out.println("KEY: " + key + " VAL: " + properties.get(key));
}
} catch (IOException e) {
e.printStackTrace();
}
}
As my 'update-strategy' is reload I can get updated whenever a new ConfigurationAdmin service is registered.
Any comment about my approach is welcome!
Related
I am facing an issue in reading the properties file in spring mvc4. To read messages I have added following in spring-servlet.xml file located under WEB-INF folder.
<context:component-scan base-package="com.test.restful.producer" />
<bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
<property name="location">
<value>classpath:application.properties</value>
</property>
<property name="ignoreUnresolvablePlaceholders" value="true"/>
In my controller class,
#Value("${MSG}")
private String msg;
i am getting msg as null. Please helpout me how to load properties file. My appilcation.properties file is available in classpath only.
I am using Spring-4.0.5
Thank you
You can try this xml for creating properties bean.
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
p:location="classpath:propertyFile.properties" name="propertiesBean"/>
Or go for non xml version as below
#PropertySource("classpath:propertyFile.properties")
public class AppConfig {
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
In my Grails app, I need access to configuration exposed by a Java class similar to the below
public class Config {
private Properties properties = new Properties();
static load(String path) {
File configFile = new File(path);
FileReader fileReader = new FileReader(configFile);
properties.load(fileReader);
}
String getProperty(String name) {
properties.getProperty(name);
}
}
I trigger the initialisation of this class in the first line of Bootstrap.groovy by calling Config.load("/conf.properties"). However, the initialization of various Spring beans needs properties that are exposed by Config, but by the time Bootstrap.groovy is executed, Spring initialization has already completed.
So I need to find a way to call Config.load() before construction of the Spring beans, is this possible? I guess there might be an event handler available in /script/_Events.groovy that I could invoke it from, but I'm not sure which handlers are available.
Unfortunately, changing the source code of Config.java isn't an option, and neither is eliminating my usage of this class.
You could try declaring a suitable bean in web-app/WEB-INF/applicationContext.xml, which is the definition of the root web application context as opposed to the GrailsApplication's internal context.
<bean id="initConfig" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetClass" value="com.example.Config" />
<property name="targetMethod" value="load" />
<property name="arguments">
<list><value>/conf.properties</value></list>
</property>
</bean>
and modify the grailsApplication bean to depend on that:
<bean id="grailsApplication" depends-on="initConfig" class="...">
In my web application i have a scenario like i need to read the values from property file and the value has to be updated dynamically in spring bean. I have created the key value pair as follows,
message1=Hi {0} welcome. Your last signed in was {1}
How to substitute the values for {0} and {1}. I have read the value using property place holder configurer in spring.
You have to do it with MessageSource
In your applicationContext add:
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basenames">
<list>
<value>messages</value>
</list>
</property>
</bean>
In your javacode you can write:
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//...
String name = context.getMessage("message1", new Object[] { "user XY", "05.05.2010" }, Locale.US);
You even can use different message.properties files for different languages: Then you have to name these files: [filename]_[languageCode] (i.e. messages_en_US.properties or messages_de_DE.properties). And change your 3rd parameter of the getMessage method.
I'm using camel:aggregate backed by jdbc and it seems it doesn't save Exchange properties. For instance, if I configure the following route and the execution is stopped once aggregation has been completed and just before execute camel:to(log) forcing the aggregation to retrieve data from database when restarted, then camel:to(log) won't print the property myProperty
<camel:route id="myRoute">
<camel:from uri="direct:in"/>
<camel:setProperty propertyName="myProperty">
<camel:constant>myPropertyValue</camel:constant>
</camel:setProperty>
<camel:aggregate strategyRef="myStrategy" aggregationRepositoryRef="myAggregationRepo" discardOnCompletionTimeout="true" completionTimeout="86400000" >
<camel:correlationExpression>
<camel:simple>${property.partlastcorrelationkey}</camel:simple>
</camel:correlationExpression>
<camel:completionPredicate>
<camel:simple>${property.partlastcorrelationwaitmore} == false</camel:simple>
</camel:completionPredicate>
<camel:to uri="log:com.test?showAll=true"/>
</camel:aggregate>
</camel:route>
My aggregation repository is configured this way:
<bean id="myAggregationRepo" class="org.apache.camel.processor.aggregate.jdbc.JdbcAggregationRepository" init-method="start" destroy-method="stop">
<property name="transactionManager" ref="transactionManager"/>
<property name="repositoryName" value="PROC_AGG"/>
<property name="dataSource" ref="oracle-ds"/>
<property name="lobHandler">
<bean class="org.springframework.jdbc.support.lob.OracleLobHandler">
<property name="nativeJdbcExtractor">
<bean class="org.springframework.jdbc.support.nativejdbc.CommonsDbcpNativeJdbcExtractor"/>
</property>
</bean>
</property>
</bean>
How can I save properties when using the Aggregator?
I'll reply myself. As seen on the code JdbcCamelCodec doesn't allow to save properties when backing the Aggregator with a database:
public final class JdbcCamelCodec {
public byte[] marshallExchange(CamelContext camelContext, Exchange exchange) throws IOException {
// use DefaultExchangeHolder to marshal to a serialized object
DefaultExchangeHolder pe = DefaultExchangeHolder.marshal(exchange, false);
// add the aggregated size property as the only property we want to retain
DefaultExchangeHolder.addProperty(pe, Exchange.AGGREGATED_SIZE, exchange.getProperty(Exchange.AGGREGATED_SIZE, Integer.class));
// add the aggregated completed by property to retain
DefaultExchangeHolder.addProperty(pe, Exchange.AGGREGATED_COMPLETED_BY, exchange.getProperty(Exchange.AGGREGATED_COMPLETED_BY, String.class));
// add the aggregated correlation key property to retain
DefaultExchangeHolder.addProperty(pe, Exchange.AGGREGATED_CORRELATION_KEY, exchange.getProperty(Exchange.AGGREGATED_CORRELATION_KEY, String.class));
// persist the from endpoint as well
if (exchange.getFromEndpoint() != null) {
DefaultExchangeHolder.addProperty(pe, "CamelAggregatedFromEndpoint", exchange.getFromEndpoint().getEndpointUri());
}
return encode(pe);
}
Basically, the problem lies on this line where false means: don't save properties.
DefaultExchangeHolder pe = DefaultExchangeHolder.marshal(exchange, false);
The headers and the body are the only ones stored on database.
I have a properties-file with a lot of values and I do not want to list them in my bean-configuration-file separately. E.g.:
<property name="foo">
<value>${foo}</value>
</property>
<property name="bar">
<value>${bar}</value>
</property>
and so on.
I imagine to inject all completely as java.util.Properties or less as a java.util.Map.
Is there a way to do so?
For Java config you can use something like this:
#Autowired #Qualifier("myProperties")
private Properties myProps;
#Bean(name="myProperties")
public Properties getMyProperties() throws IOException {
return PropertiesLoaderUtils.loadProperties(
new ClassPathResource("/myProperties.properties"));
}
You can also have multiple properties this way, if you assign a unique bean name (Qualifier) to each instance.
Yes, you can use <util:properties> to load a properties file and declare the resulting java.util.Properties object as a bean. You can then inject that as you would any other bean property.
See section C.2.2.3 of the Spring manual, and their example:
<util:properties id="myProps" location="classpath:com/foo/jdbc-production.properties"
Remember to declare the util: namespace as per these instructions.
For Java Config, use PropertiesFactoryBean:
#Bean
public Properties myProperties() {
PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
propertiesFactoryBean.setLocation(new ClassPathResource("/myProperties.properties"));
Properties properties = null;
try {
propertiesFactoryBean.afterPropertiesSet();
properties = propertiesFactoryBean.getObject();
} catch (IOException e) {
log.warn("Cannot load properties file.");
}
return properties;
}
And then, set the properties object:
#Bean
public AnotherBean myBean() {
AnotherBean myBean = new AnotherBean();
...
myBean.setProperties(myProperties());
...
}
Hope this helps for those interested in Java Config way.
It's possible with the PropertyOverrideConfigurer mechanism:
<context:property-override location="classpath:override.properties"/>
Properties file:
beanname1.foo=foovalue
beanname2.bar.baz=bazvalue
The mechanism is explained in the section 3.8.2.2 Example: the PropertyOverrideConfigurer
This is an echo of #skaffman's response in this SO question. I am adding more details to help others and myself when I try to solve this in the future.
There are three ways to inject the property file
Method 1
<bean id="myProps" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="locations">
<list>
<value>classpath:com/foo/jdbc-production.properties</value>
</list>
</property>
</bean>
Reference ( link )
Method 2
<?xml version="1.0" encoding="UTF-8"?>
<beans
...
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="...
...
http://www.springframework.org/schema/util/spring-util.xsd"/>
<util:properties id="myProps" location="classpath:com/foo/jdbc-production.properties"/>
Reference ( link )
Method 3
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:com/foo/jdbc-production.properties" />
</bean>
Reference ( link )
Essentially, all the methods can create a Properties bean out of the properties file. You may even directly inject a value from the property file by using #Value injector
#Value("#{myProps[myPropName]}")
private String myField;