Require a service to be active before a bundle is started - osgi

I have written a BundleActivator which should update certain configurations before its bundle starts. I need the ConfigurationAdmin service, but I get a null ServiceReference from the BundleContext in the start method of the BundleActivator.
The BundleActivator extends following abstract class and only implements the specific update logic:
public abstract class AbstractConfigUpdater implements BundleActivator {
private ServiceReference<ConfigurationAdmin> configurationAdminServiceReference;
#Override
public void start(final BundleContext context) throws Exception {
configurationAdminServiceReference = context.getServiceReference(ConfigurationAdmin.class);
final ConfigurationAdmin configurationAdmin = context.getService(configurationAdminServiceReference);
final Configuration[] configurations =
configurationAdmin.listConfigurations(getFilter());
if (configurations != null) {
for (final Configuration configuration : configurations) {
final Dictionary<String, Object> properties = configuration.getProperties();
if (updateProperties(properties)) {
configuration.update(properties);
}
}
}
}
protected abstract String getFilter();
/**
* Updates the properties if needed.
*
* #param properties
* the configuration properties
* #return if any modifications to the Dictionary were made
*/
protected abstract boolean updateProperties(final Dictionary<String, Object> properties);
#Override
public void stop(final BundleContext context) throws Exception {
context.ungetService(configurationAdminServiceReference);
}
}
I have added an annotation to the concrete BundleActivator to generate a manifest header to require the ConfigurationAdmin service to be available to the bundle:
#RequireCapability(filter = "(objectClass=org.osgi.service.cm.ConfigurationAdmin)",
ns = "osgi.service",
resolution = Resolution.mandatory)
The manifest header is generated, but I still get a null ServiceReference. How should I fix this? Or is there an alternative approach I could take to update configurations before their components are started?

I don't know if this could help, but you can develop a org.osgi.service.cm.ConfigurationPlugin to intercept all the properties that are injected at runtime and modify them:
public class MyConfigurationPlugin implements BundleActivator, ConfigurationPlugin {
ServiceRegistration<ConfigurationPlugin> configPluginRef;
#Override
public void start(BundleContext context) throws Exception {
//... init the config plugin
Map<String,String> properties = new HashMap<>();
configPluginRef = context.registerService(
ConfigurationPlugin.class,
this,
new Hashtable<>(properties));
}
#Override
public void modifyConfiguration(ServiceReference<?> reference,
Dictionary<String, Object> properties) {
/*
* View and possibly modify a set of configuration properties
* before they are sent to the Managed Service or the Managed Service Factory.
*/
}
}
Of course the Declarative Service approach is a way far simpler option:
#Component (
service= {},
configurationPid={
configPid1,
configPid2,
...
})
public class MyComponent {
#Activate
public void activate(BundleContext context, Map<String, String> properties) {
}
#Modified
public void updated(BundleContext context, Map<String, String> properties) {
// Called when properties change
}
}
but in this case you cannot alter properties values: you can only react to properties changes.

You can use OSGi ServiceTracker to wait and retrieve the service from the service registry.
For example,
import org.osgi.framework.Constants
import org.osgi.framework.Filter;
import org.osgi.util.tracker.ServiceTracker;
import org.osgi.service.cm.ConfigurationAdmin;
...
private static final long TIMEOUT_MILLIS = 10000;
#Override
public void start(final BundleContext context) throws Exception {
Filter filter = context.createFilter("(" + Constants.OBJECTCLASS + "=org.osgi.service.cm.ConfigurationAdmin)");
ServiceTracker<?, ?> configurationAdminTracker = new ServiceTracker<>(context, filter, null);
configurationAdminTracker.open();
ConfigurationAdmin configurationAdmin = (ConfigurationAdmin) configurationAdminTracker.waitForService(TIMEOUT_MILLIS);
configurationAdminTracker.close();
if (configurationAdmin == null) {
// Not found
}
...
}

Related

Injecting spring bean (service layer class) into ResourceBundle

I created a class using ResourceBundle interface as shown below. This class is dependent on another class. The implementation class for ResourceBundle (QuestionnaireSource as shown below) always has null as dependencies. No matter if I use setter or constructor injection.
Could someone please help me with this issue. I am I missing some configuration here.
#Component
public class QuestionnaireSource extends ResourceBundle {
private final QuestionnaireCache questionnaireCache;
private static final Object lockObject = new Object();
#Override
protected Object handleGetObject(String key) {
// Gets an object for the given key from this resource bundle.
// Returns null if this resource bundle does not contain an object for the given key 0
Object value = null;
try {
value = getString(key, LocaleContextHolder.getLocale());
} catch (Exception ignored) {
}
return value;
}
public Questionnaire getString(String key, Locale locale) {
Locale l = safeLocale(locale);
return getResources(l).get(key);
}
private Locale safeLocale(Locale l) {
if (l.getLanguage().equalsIgnoreCase("DE")) {
return Locale.GERMAN;
} else {
return Locale.ENGLISH;
}
}
protected Map<String, Questionnaire> getResources(Locale locale) {
synchronized (lockObject) {
return questionnaireCache.getQuestionnaireCache().get(locale.getLanguage().toUpperCase());
}
}
#Override
public Enumeration<String> getKeys() {
return null;
}
public QuestionnaireSource(QuestionnaireCache questionnaireCache) {
super();
this.questionnaireCache = questionnaireCache;
}
}
Update:
I found that even simple dependency injection in resourceBundle is failing.
UPdate2:
The way I am using in the main class is as follows:
// ResourceBundle test here
System.out.println("Test here for resource bundle");
Locale locale = new Locale("de", "DE");
ResourceBundle bundle = ResourceBundle.getBundle("com.app.util.QuestionnaireSource", locale);
System.out.println(bundle.getString("some.test.string"));
Update3
I am writing a simple example to convey the scenario:
Some service class
#Service
public class SomeServiceTest {
public String testMethod(){
return "test here and complete";
}
}
Some example implementation of resource bundle
#Component
public class MyResourceBundle extends ResourceBundle {
private final SomeServiceTest someServiceTest;
#Autowired
public MyResourceBundle(SomeServiceTest someServiceTest) {
this.someServiceTest = someServiceTest;
}
#Override
protected Object handleGetObject(String key) {
if(key.equals("test"))
return "test";
return null;
}
#Override
public Enumeration<String> getKeys() {
return null;
}
}
Main.java
main(){
// ResourceBundle test here
System.out.println("Test here for resource bundle");
Locale locale = new Locale("de", "DE");
ResourceBundle bundle = ResourceBundle.getBundle("com.app.util.MyResourceBundle", locale);
System.out.println(bundle.getString("test"));
}
Update4:
I changed the annotation on classes as mentioned by on this post https://www.baeldung.com/spring-inject-bean-into-unmanaged-objects
but still I have the null dependency injection for SomeServiceTest class. The changes are as shown below.
SomeServiceTest.java
#Service
public class SomeServiceTest {
public String testMethod(){
return "test here and complete";
}
}
MyResourceBundle.java
#Configurable
public class MyResourceBundle extends ResourceBundle {
#Autowired
private SomeServiceTest someServiceTest;
public MyResourceBundle() {
}
#Override
protected Object handleGetObject(String key) {
if(key.equals("test"))
return someServiceTest.testMethod();
return null;
}
#Override
public Enumeration<String> getKeys() {
return null;
}
}
still SomeServiceTest class is null.
Can you please post an example on how you are using this class? Is it you (your code) or spring who instanciate it (on startup)?
#Component only works for beans which Spring instanciate. If you want to inject stuff in classes you instanciate in you code you can annotate the class with #Configurable.
Please see https://www.baeldung.com/spring-inject-bean-into-unmanaged-objects for some examples.
Make sure you have initialized the spring context
If you are using spring boot
You can get the application context after it starts and use it to get the bean you want
For example
public static void main(String[] args) {
ConfigurableApplicationContext run = SpringApplication.run(YouApplication.class, args);
MyResourceBundle resConfig = run.getBean("myResourceBundle", MyResourceBundle .class);
resConfig.handleGetObject("test");
}
Unfortunately ResourceBundle.getBundle does not initialize the spring application context

Autowiring not working in springboot application

I am trying to create a Spring boot application with JFrame. I can see my beans in applicationContext but they are not getting autowired. I am unable to find the reason for this issue. Can someone help me with this?
Here is the code:
JavauiApplication - it is showing both userManager and userNameRepository is beans
#SpringBootApplication
public class JavauiApplication implements CommandLineRunner {
#Autowired
private ApplicationContext appContext;
public static void main(String[] args) {
new SpringApplicationBuilder(JavauiApplication.class).headless(false).run(args);
java.awt.EventQueue.invokeLater(() -> new InputNameForm().setVisible(true));
}
#Override
public void run(String... args) throws Exception {
String[] beans = appContext.getBeanDefinitionNames();
Arrays.sort(beans);
for (String bean : beans) {
System.out.println(bean);
}
}
}
InputNameForm.java -> userManager coming null
#Component
public class InputNameForm extends javax.swing.JFrame {
/**
* Creates new form InputNameForm
*/
public InputNameForm() {
initComponents();
}
#Autowired
UserManager userManager;
private void submitButtonActionPerformed(java.awt.event.ActionEvent evt) {
userManager.setName(firstName.getText(), lastName.getText());
}
/**
* #param args the command line arguments
*/
public static void main(String args[]) {
try {
for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
if ("Nimbus".equals(info.getName())) {
javax.swing.UIManager.setLookAndFeel(info.getClassName());
break;
}
}
} catch (ClassNotFoundException ex) {
java.util.logging.Logger.getLogger(InputNameForm.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
}
/* Create and display the form */
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new InputNameForm().setVisible(true);
}
});
}
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JTextField firstName;
private javax.swing.JLabel firstNameLabel;
private javax.swing.JTextField lastName;
private javax.swing.JLabel lastNameLabel;
private javax.swing.JButton submitButton;
// End of variables declaration//GEN-END:variables
}
UserManager.java -> userNameRepository is coming null
#Component
public class UserManager {
#Autowired
UserNameRepository userNameRepository;
public void setName(String firstName, String lastName) {
userNameRepository.save(new UserName(firstName, lastName));
System.out.println(userNameRepository.findAllByFirstName(firstName));
}
}
It's a very common problem and it occurs because newcomers don't understand how the IoC container works.
Firstly, BeanDefinitionReader reads metadata about your beans from XML, Annotations(#Component, #Service etc), JavaConfig or Groovy script.
There are several BeanPostProcessor's which is responsible for reading all of these Spring annotation you're writing(#Autowired etc).
BeanFactory creates all BeanPostProcessor's then it creates all of your beans.
What happen if you create your bean with #Autowired dependencies via new operator? Nothing, because it isn't actually a bean. The object you created isn't related to IoC container. You may have the bean already in your ApplicationContext if you marked it with #Component(for example) but the object which was created via new operator wont be processed by Spring(annotations won't work).
Hope this helps.
PS: The lifecycle is simplified.
I had the same problem few days ago. What I undertood was that GUI builders like the one that comes with netbeans will automatically create components using new keyword. This means that those components won't be manage by spring. The code usually loks like this:
private void initComponents() {
jPanel1 = new javax.swing.JPanel(); //This component will not be managed by spring.
//...
}
You could use the following class provided here, to make it work.
#Component
public class BeanProvider {
private static ApplicationContext applicationContext;
// Autowires the specified object in the spring context
public static void autowire(Object object) {
applicationContext.getAutowireCapableBeanFactory().autowireBean(object);
}
#Autowired
private void setApplicationContext(ApplicationContext applicationContext) {
BeanProvider.applicationContext = applicationContext;
}
}
The top level SwingApp class:
#SpringBootApplication
public class SwingApp implements CommandLineRunner {
public static void main(String[] args) {
new SpringApplicationBuilder(SwingApp.class)
.headless(false).bannerMode(Banner.Mode.OFF).run(args);
}
#Override
public void run(String... args) throws Exception {
SwingUtilities.invokeLater(() -> {
MainFrame frame = new MainFrame();
frame.setVisible(true);
});
}
}
The MainFrame class:
public class MainFrame extends javax.swing.JFrame {
public MainFrame() {
initComponents();
}
private void initComponents() {
//Gui Builder generated code. Bean not managed by spring.
//Thus, autowired inside CustomPanel won't work if you rely on ComponentScan.
jPanel1 = new CustomJPanel();
//...
}
private CustomJPanel jPanel1;
}
The panel class where you want to autowire things:
//#Component //not needed since it wont work with gui generated code.
public class CustomJPanel extends javax.swing.JPanel{
#Autowired
private SomeRepository someRepository
public CustomJPanel(){
BeanProvider.autowire(this); //use someRepository somewhere after this line.
}
}
I have the same problem in a JavaFx project. Service and Component annotated classes were null in UI controllers even if it was shown in context that it was created. Below code worked for me
#Component
public class FxmlLoaderWithContext {
private final ApplicationContext context;
#Autowired
public FxmlLoaderWithContext(ApplicationContext context) {
this.context = context;
FXMLLoader fxmlloader = new FXMLLoader();
fxmlloader.setControllerFactory(context::getBean); //this row ensure services and components to be autowired
}
}
I think it returns null because you using command new to create object, such as new InputNameForm(). When creating object like that, the object isn't managed by Spring. That's why autowired not working.
The solution is registering your class as a bean.
You can use a class like in here.
#Component
public class BeanProvider {
private static ApplicationContext applicationContext;
public static void autowire(Object object) {
applicationContext.getAutowireCapableBeanFactory().autowireBean(object);
}
#Autowired
private void setApplicationContext(ApplicationContext applicationContext) {
BeanProvider.applicationContext = applicationContext;
}
}
And then, in your class InputNameForm constructor, call this:
class InputNameForm() {
BeanProvider.autowire(this);
...
}
And that's it. Spring will take care the rest.

Spring Boot Apache Camel Routes testing

I have a Springboot application, where I have some Camel routes configured.
public class CamelConfig {
private static final Logger LOG = LoggerFactory.getLogger(CamelConfig.class);
#Value("${activemq.broker.url:tcp://localhost:61616}")
String brokerUrl;
#Value("${activemq.broker.maxconnections:1}")
int maxConnections;
#Bean
ConnectionFactory jmsConnectionFactory() {
PooledConnectionFactory pooledConnectionFactory = new PooledConnectionFactory(new ActiveMQConnectionFactory(brokerUrl));
pooledConnectionFactory.setMaxConnections(maxConnections);
return pooledConnectionFactory;
}
#Bean
public RoutesBuilder route() {
LOG.info("Initializing camel routes......................");
return new SpringRouteBuilder() {
#Override
public void configure() throws Exception {
from("activemq:testQueue")
.to("bean:queueEventHandler?method=handleQueueEvent");
}
};
}
}
I want to test this route from activemq:testQueue to queueEventHandler::handleQueueEvent.
I tried different things mentioned here http://camel.apache.org/camel-test.html, but doesn't seem to get it working.
I am trying to do something like this:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = {CamelConfig.class, CamelTestContextBootstrapper.class})
public class CamelRouteConfigTest {
#Produce(uri = "activemq:testQueue")
protected ProducerTemplate template;
#Test
public void testSendMatchingMessage() throws Exception {
template.sendBodyAndHeader("testJson", "foo", "bar");
// Verify handleQueueEvent(...) method is called on bean queueEventHandler by mocking
}
But my ProducerTemplate is always null. I tried auto-wiring CamelContext, for which I get an exception saying it cannot resolve camelContext. But that can be resolved by adding SpringCamelContext.class to #SpringBootTest classes. But my ProducerTemplate is still null.
Please suggest. I am using Camel 2.18 and Spring Boot 1.4.
In Camel 2.22.0 and ongoing, which supports Spring Boot 2 you can use the following template to test your routes with Spring Boot 2 support:
#RunWith(CamelSpringRunner.class)
#SpringBootTest(webEnvironment = WebEnvironment.NONE, classes = {
Route1.class,
Route2.class,
...
})
#EnableAutoConfiguration
#DisableJmx
#DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
public class RouteTest {
#TestConfiguration
static class Config {
#Bean
CamelContextConfiguration contextConfiguration() {
return new CamelContextConfiguration() {
#Override
public void beforeApplicationStart(CamelContext camelContext) {
// configure Camel here
}
#Override
public void afterApplicationStart(CamelContext camelContext) {
// Start your manual routes here
}
};
}
#Bean
RouteBuilder routeBuilder() {
return new RouteBuilder() {
#Override
public void configure() {
from("direct:someEndpoint").to("mock:done");
}
};
}
// further beans ...
}
#Produce(uri = "direct:start")
private ProducerTemplate template;
#EndpointInject(uri = "mock:done")
private MockEndpoint mockDone;
#Test
public void testCamelRoute() throws Exception {
mockDone.expectedMessageCount(1);
Map<String, Object> headers = new HashMap<>();
...
template.sendBodyAndHeaders("test", headers);
mockDone.assertIsSatisfied();
}
}
Spring Boot distinguishes between #Configuration and #TestConfiguration. The primer one will replace any existing configuration, if annotated on a top-level class, while #TestConfiguration will be run in addition to the other configurations.
Further, in larger projects you might run into auto-configuration issues as you can't rely on Spring Boot 2 to configure your custom database pooling or what not correctly or in cases where you have a specific directory structure and the configurations are not located within a direct ancestor directory. In that case it is proabably preferable to omit the #EnableAutoConfiguration annotation. In order to tell Spring to still auto-configure Camel you can simply pass CamelAutoConfiguration.class to the classes mentioned in #SpringBootTest
#SpringBootTest(webEnvironment = WebEnvironment.NONE, classes = {
Route1.class,
Route2.class,
RouteTest.Config.class,
CamelAutoConfiguration.class
}
As no automatic configuration is performed, Spring won't load the test configuration inside your test class nor initialize Camel as well. By adding those configs to the boot classes manually Spring will do it for you.
For one route with MQ and Spring Boot like this:
#Component
public class InboundRoute extends RouteBuilder {
#Override
public void configure() {
JaxbDataFormat personDataFormat = new JaxbDataFormat();
personDataFormat.setContextPath(Person.class.getPackage().getName());
personDataFormat.setPrettyPrint(true);
from("direct:start").id("InboundRoute")
.log("inbound route")
.marshal(personDataFormat)
.to("log:com.company.app?showAll=true&multiline=true")
.convertBodyTo(String.class)
.inOnly("mq:q.empi.deim.in")
.transform(constant("DONE"));
}
}
I use adviceWith in order to replace the endpoint and use only mocks:
#RunWith(CamelSpringBootRunner.class)
#UseAdviceWith
#SpringBootTest(classes = InboundApp.class)
#MockEndpoints("mock:a")
public class InboundRouteCamelTest {
#EndpointInject(uri = "mock:a")
private MockEndpoint mock;
#Produce(uri = "direct:start")
private ProducerTemplate template;
#Autowired
private CamelContext context;
#Test
public void whenInboundRouteIsCalled_thenSuccess() throws Exception {
mock.expectedMinimumMessageCount(1);
RouteDefinition route = context.getRouteDefinition("InboundRoute");
route.adviceWith(context, new AdviceWithRouteBuilder() {
#Override
public void configure() {
weaveByToUri("mq:q.empi.deim.in").replace().to("mock:a");
}
});
context.start();
String response = (String) template.requestBodyAndHeader("direct:start",
getSampleMessage("/SimplePatient.xml"), Exchange.CONTENT_TYPE, MediaType.APPLICATION_XML);
assertThat(response).isEqualTo("DONE");
mock.assertIsSatisfied();
}
private String getSampleMessage(String filename) throws Exception {
return IOUtils
.toString(this.getClass().getResourceAsStream(filename), StandardCharsets.UTF_8.name());
}
}
I use the following dependencies: Spring Boot 2.1.4-RELEASE and Camel 2.23.2. The complete source code is available on Github.
This is how I did this finally:
#RunWith(SpringRunner.class)
public class CamelRouteConfigTest extends CamelTestSupport {
private static final Logger LOG = LoggerFactory.getLogger(CamelRouteConfigTest.class);
private static BrokerService brokerSvc = new BrokerService();
#Mock
private QueueEventHandler queueEventHandler;
#BeforeClass
// Sets up an embedded broker
public static void setUpBroker() throws Exception {
brokerSvc.setBrokerName("TestBroker");
brokerSvc.addConnector("tcp://localhost:61616");
brokerSvc.setPersistent(false);
brokerSvc.setUseJmx(false);
brokerSvc.start();
}
#Override
protected RoutesBuilder createRouteBuilder() throws Exception {
return new CamelConfig().route();
}
// properties in .yml has to be loaded manually. Not sure of .properties file
#Override
protected Properties useOverridePropertiesWithPropertiesComponent() {
YamlPropertySourceLoader loader = new YamlPropertySourceLoader();
try {
PropertySource<?> applicationYamlPropertySource = loader.load(
"properties", new ClassPathResource("application.yml"),null);// null indicated common properties for all profiles.
Map source = ((MapPropertySource) applicationYamlPropertySource).getSource();
Properties properties = new Properties();
properties.putAll(source);
return properties;
} catch (IOException e) {
LOG.error("application.yml file cannot be found.");
}
return null;
}
#Override
protected JndiRegistry createRegistry() throws Exception {
JndiRegistry jndi = super.createRegistry();
MockitoAnnotations.initMocks(this);
jndi.bind("queueEventHandler", queueEventHandler);
return jndi;
}
#Test
// Sleeping for a few seconds is necessary, because this line template.sendBody runs in a different thread and
// CamelTest takes a few seconds to do the routing.
public void testRoute() throws InterruptedException {
template.sendBody("activemq:productpushevent", "HelloWorld!");
Thread.sleep(2000);
verify(queueEventHandler, times(1)).handleQueueEvent(any());
}
#AfterClass
public static void shutDownBroker() throws Exception {
brokerSvc.stop();
}
}
Did you try using Camel test runner?
#RunWith(CamelSpringJUnit4ClassRunner.class)
If you are using camel-spring-boot dependency, you may know that it uses auto configuration to setup Camel:
CamelAutoConfiguration.java
It means that you may also need to add #EnableAutoConfiguration to your test.

Spring Profiles not recognized in a Web Application

I'm trying to implement Spring Profiles to load a specific class implementation based on the profile specified. The profile to use is specified as a property (spring.profiles.active) within a properties file included in the classpath.
Source here: https://github.com/overattribution/spring-profile-test
public interface MyService {
public void doSomething();
}
#Service
#Profile("preprod")
public class MyServicePreProdImpl implements MyService {
private final Logger LOG = LoggerFactory.getLogger(MyServicePreProdImpl.class);
#Override
public void doSomething() {
LOG.debug("Doing something in " + MyServicePreProdImpl.class.getName());
}
}
#Service
#Profile("prod")
public class MyServiceProdImpl implements MyService {
private final Logger LOG = LoggerFactory.getLogger(MyServiceProdImpl.class);
#Override
public void doSomething() {
LOG.debug("Doing something in " + MyServiceProdImpl.class.getName());
}
}
However, I'm getting the following error:
No qualifying bean of type [com.example.profileservice.MyService] found for dependency.
What am I missing?
UPDATE 1:
I'm manually setting the active profile during web application initialization, but I'm still getting the "No qualifying bean found" error. Changes can be seen here: https://github.com/overattribution/spring-profile-test/commit/09175a10b28ea8e5a08b43ad1416431bcf094c9d
Ok I got it to work. Profiles need to be set within the root context (as opposed to servlet context) during web application initialization. I have done so in my WebAppInitializer class like so:
https://github.com/overattribution/spring-profile-test/blob/f895a8bc67dc1f6ba2fcedb58b73a19cc5cf8cf7/src/main/java/com/example/config/WebAppInitializer.java
public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
/**
* Overriding to include setting active profile.
*/
#Override
protected WebApplicationContext createRootApplicationContext() {
Class<?>[] configClasses = getRootConfigClasses();
if (!ObjectUtils.isEmpty(configClasses)) {
AnnotationConfigWebApplicationContext rootAppContext = new AnnotationConfigWebApplicationContext();
String[] activeProfiles = getActiveProfiles();
rootAppContext.getEnvironment().setActiveProfiles(activeProfiles);
rootAppContext.register(configClasses);
return rootAppContext;
}
else {
return null;
}
}
#Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[] {
ApplicationConfig.class
};
}
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[] {
WebMvcConfig.class
};
}
#Override
protected Filter[] getServletFilters() {
CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
characterEncodingFilter.setEncoding("UTF-8");
characterEncodingFilter.setForceEncoding(true);
return new Filter[] {characterEncodingFilter};
}
protected String[] getActiveProfiles() {
PropertySource propertySource = null;
try {
propertySource = new ResourcePropertySource("classpath:application.properties");
String profilesString = (String) propertySource.getProperty(AbstractEnvironment.ACTIVE_PROFILES_PROPERTY_NAME);
return profilesString.split(",");
} catch (IOException e) {
throw new ResourceAccessException("application.properties is not available on the classpath");
}
}
}
A profile is a named logical grouping that may be activated programmatically via ConfigurableEnvironment.setActiveProfiles(java.lang.String...) or declaratively through setting the spring.profiles.active property, usually through JVM system properties, as an environment variable, or for web applications as a Servlet context parameter in web.xml.
I would say that it is not possible to specify active profile as a property within your properties file unless you use Spring Boot that also enables you to set the active profile in application.properties file.
Try to use one of the options above.

Access properties file programmatically with Spring?

We use the code below to inject Spring beans with properties from a properties file.
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations" value="classpath:/my.properties"/>
</bean>
<bean id="blah" class="abc">
<property name="path" value="${the.path}"/>
</bean>
Is there a way we can access the properties programmatically? I'm trying to do some code without dependency injection. So I'd like to just have some code like this:
PropertyPlaceholderConfigurer props = new PropertyPlaceholderConfigurer();
props.load("classpath:/my.properties");
props.get("path");
How about PropertiesLoaderUtils?
Resource resource = new ClassPathResource("/my.properties");
Properties props = PropertiesLoaderUtils.loadProperties(resource);
If all you want to do is access placeholder value from code, there is the #Value annotation:
#Value("${settings.some.property}")
String someValue;
To access placeholders From SPEL use this syntax:
#('${settings.some.property}')
To expose configuration to views that have SPEL turned off, one can use this trick:
package com.my.app;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.stereotype.Component;
#Component
public class PropertyPlaceholderExposer implements Map<String, String>, BeanFactoryAware {
ConfigurableBeanFactory beanFactory;
#Override
public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = (ConfigurableBeanFactory) beanFactory;
}
protected String resolveProperty(String name) {
String rv = beanFactory.resolveEmbeddedValue("${" + name + "}");
return rv;
}
#Override
public String get(Object key) {
return resolveProperty(key.toString());
}
#Override
public boolean containsKey(Object key) {
try {
resolveProperty(key.toString());
return true;
}
catch(Exception e) {
return false;
}
}
#Override public boolean isEmpty() { return false; }
#Override public Set<String> keySet() { throw new UnsupportedOperationException(); }
#Override public Set<java.util.Map.Entry<String, String>> entrySet() { throw new UnsupportedOperationException(); }
#Override public Collection<String> values() { throw new UnsupportedOperationException(); }
#Override public int size() { throw new UnsupportedOperationException(); }
#Override public boolean containsValue(Object value) { throw new UnsupportedOperationException(); }
#Override public void clear() { throw new UnsupportedOperationException(); }
#Override public String put(String key, String value) { throw new UnsupportedOperationException(); }
#Override public String remove(Object key) { throw new UnsupportedOperationException(); }
#Override public void putAll(Map<? extends String, ? extends String> t) { throw new UnsupportedOperationException(); }
}
And then use the exposer to expose properties to a view:
<bean class="org.springframework.web.servlet.view.UrlBasedViewResolver" id="tilesViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.tiles2.TilesView"/>
<property name="attributesMap">
<map>
<entry key="config">
<bean class="com.my.app.PropertyPlaceholderExposer" />
</entry>
</map>
</property>
</bean>
Then in view, use the exposed properties like this:
${config['settings.some.property']}
This solution has the advantage that you can rely on standard placeholder
implementation injected by the context:property-placeholder tag.
Now as a final note, if you really need a to capture all placeholder properties and their values, you have to pipe them through StringValueResolver to make sure that placeholders work inside the property values as expected. The following code will do that.
package com.my.app;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
import org.springframework.util.StringValueResolver;
public class AppConfig extends PropertyPlaceholderConfigurer implements Map<String, String> {
Map<String, String> props = new HashMap<String, String>();
#Override
protected void processProperties(ConfigurableListableBeanFactory beanFactory, Properties props)
throws BeansException {
this.props.clear();
for (Entry<Object, Object> e: props.entrySet())
this.props.put(e.getKey().toString(), e.getValue().toString());
super.processProperties(beanFactory, props);
}
#Override
protected void doProcessProperties(ConfigurableListableBeanFactory beanFactoryToProcess,
StringValueResolver valueResolver) {
super.doProcessProperties(beanFactoryToProcess, valueResolver);
for(Entry<String, String> e: props.entrySet())
e.setValue(valueResolver.resolveStringValue(e.getValue()));
}
// Implement map interface to access stored properties
#Override public Set<String> keySet() { return props.keySet(); }
#Override public Set<java.util.Map.Entry<String, String>> entrySet() { return props.entrySet(); }
#Override public Collection<String> values() { return props.values(); }
#Override public int size() { return props.size(); }
#Override public boolean isEmpty() { return props.isEmpty(); }
#Override public boolean containsValue(Object value) { return props.containsValue(value); }
#Override public boolean containsKey(Object key) { return props.containsKey(key); }
#Override public String get(Object key) { return props.get(key); }
#Override public void clear() { throw new UnsupportedOperationException(); }
#Override public String put(String key, String value) { throw new UnsupportedOperationException(); }
#Override public String remove(Object key) { throw new UnsupportedOperationException(); }
#Override public void putAll(Map<? extends String, ? extends String> t) { throw new UnsupportedOperationException(); }
}
I have done this and it has worked.
Properties props = PropertiesLoaderUtils.loadAllProperties("my.properties");
PropertyPlaceholderConfigurer props2 = new PropertyPlaceholderConfigurer();
props2.setProperties(props);
That should work.
CREDIT: Programmatic access to properties in Spring without re-reading the properties file
I've found a nice implementation of accessing the properties programmatically in spring without reloading the same properties that spring has already loaded. [Also, It is not required to hardcode the property file location in the source]
With these changes, the code looks cleaner & more maintainable.
The concept is pretty simple. Just extend the spring default property placeholder (PropertyPlaceholderConfigurer) and capture the properties it loads in the local variable
public class SpringPropertiesUtil extends PropertyPlaceholderConfigurer {
private static Map<String, String> propertiesMap;
// Default as in PropertyPlaceholderConfigurer
private int springSystemPropertiesMode = SYSTEM_PROPERTIES_MODE_FALLBACK;
#Override
public void setSystemPropertiesMode(int systemPropertiesMode) {
super.setSystemPropertiesMode(systemPropertiesMode);
springSystemPropertiesMode = systemPropertiesMode;
}
#Override
protected void processProperties(ConfigurableListableBeanFactory beanFactory, Properties props) throws BeansException {
super.processProperties(beanFactory, props);
propertiesMap = new HashMap<String, String>();
for (Object key : props.keySet()) {
String keyStr = key.toString();
String valueStr = resolvePlaceholder(keyStr, props, springSystemPropertiesMode);
propertiesMap.put(keyStr, valueStr);
}
}
public static String getProperty(String name) {
return propertiesMap.get(name).toString();
}
}
Usage Example
SpringPropertiesUtil.getProperty("myProperty")
Spring configuration changes
<bean id="placeholderConfigMM" class="SpringPropertiesUtil">
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE"/>
<property name="locations">
<list>
<value>classpath:myproperties.properties</value>
</list>
</property>
</bean>
Hope this helps to solve the problems you have
You can also use either the spring utils, or load properties via the PropertiesFactoryBean.
<util:properties id="myProps" location="classpath:com/foo/myprops.properties"/>
or:
<bean id="myProps" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="location" value="classpath:com/foo/myprops.properties"/>
</bean>
Then you can pick them up in your application with:
#Resource(name = "myProps")
private Properties myProps;
and additionally use these properties in your config:
<context:property-placeholder properties-ref="myProps"/>
This is also in the docs: http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#xsd-config-body-schemas-util-properties
Create a class like below
package com.tmghealth.common.util;
import java.util.Properties;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
#Component
#Configuration
#PropertySource(value = { "classpath:/spring/server-urls.properties" })
public class PropertiesReader extends PropertyPlaceholderConfigurer {
#Override
protected void processProperties(
ConfigurableListableBeanFactory beanFactory, Properties props)
throws BeansException {
super.processProperties(beanFactory, props);
}
}
Then wherever you want to access a property use
#Autowired
private Environment environment;
and getters and setters then access using
environment.getProperty(envName
+ ".letter.fdi.letterdetails.restServiceUrl");
-- write getters and setters in the accessor class
public Environment getEnvironment() {
return environment;
}`enter code here`
public void setEnvironment(Environment environment) {
this.environment = environment;
}
You can get your properties through Environment class. As documentation stands:
Properties play an important role in almost all applications, and may originate from a variety of sources: properties files, JVM system properties, system environment variables, JNDI, servlet context parameters, ad-hoc Properties objects, Maps, and so on. The role of the environment object with relation to properties is to provide the user with a convenient service interface for configuring property sources and resolving properties from them.
Having Environment as a env variable, simply call:
env.resolvePlaceholders("${your-property:default-value}")
You can get your 'raw' properties through:
env.getProperty("your-property")
It will search through all properties source that spring has registered.
You can either obtain Environment through:
inject ApplicationContext by implementing ApplicationContextAware and then call getEnvironment() on context
implement EnvironmentAware.
It's obtain through implementation of a class because properties are resolved on early stage of application startup, as they may be required for bean construction.
Read more on documentation: spring Environment documentation
As you know the newer versions of Spring don't use the PropertyPlaceholderConfigurer and now use another nightmarish construct called PropertySourcesPlaceholderConfigurer. If you're trying to get resolved properties from code, and wish the Spring team gave us a way to do this a long time ago, then vote this post up! ... Because this is how you do it the new way:
Subclass PropertySourcesPlaceholderConfigurer:
public class SpringPropertyExposer extends PropertySourcesPlaceholderConfigurer {
private ConfigurableListableBeanFactory factory;
/**
* Save off the bean factory so we can use it later to resolve properties
*/
#Override
protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess,
final ConfigurablePropertyResolver propertyResolver) throws BeansException {
super.processProperties(beanFactoryToProcess, propertyResolver);
if (beanFactoryToProcess.hasEmbeddedValueResolver()) {
logger.debug("Value resolver exists.");
factory = beanFactoryToProcess;
}
else {
logger.error("No existing embedded value resolver.");
}
}
public String getProperty(String name) {
Object propertyValue = factory.resolveEmbeddedValue(this.placeholderPrefix + name + this.placeholderSuffix);
return propertyValue.toString();
}
}
To use it, make sure to use your subclass in your #Configuration and save off a reference to it for later use.
#Configuration
#ComponentScan
public class PropertiesConfig {
public static SpringPropertyExposer commonEnvConfig;
#Bean(name="commonConfig")
public static PropertySourcesPlaceholderConfigurer commonConfig() throws IOException {
commonEnvConfig = new SpringPropertyExposer(); //This is a subclass of the return type.
PropertiesFactoryBean commonConfig = new PropertiesFactoryBean();
commonConfig.setLocation(new ClassPathResource("META-INF/spring/config.properties"));
try {
commonConfig.afterPropertiesSet();
}
catch (IOException e) {
e.printStackTrace();
throw e;
}
commonEnvConfig.setProperties(commonConfig.getObject());
return commonEnvConfig;
}
}
Usage:
Object value = PropertiesConfig.commonEnvConfig.getProperty("key.subkey");
This help me:
ApplicationContextUtils.getApplicationContext().getEnvironment()
Here is another sample .
XmlBeanFactory factory = new XmlBeanFactory(new FileSystemResource("beans.xml"));
PropertyPlaceholderConfigurer cfg = new PropertyPlaceholderConfigurer();
cfg.setLocation(new FileSystemResource("jdbc.properties"));
cfg.postProcessBeanFactory(factory);
This will resolve any nested properties.
public class Environment extends PropertyPlaceholderConfigurer {
/**
* Map that hold all the properties.
*/
private Map<String, String> propertiesMap;
/**
* Iterate through all the Property keys and build a Map, resolve all the nested values before building the map.
*/
#Override
protected void processProperties(ConfigurableListableBeanFactory beanFactory, Properties props) throws BeansException {
super.processProperties(beanFactory, props);
propertiesMap = new HashMap<String, String>();
for (Object key : props.keySet()) {
String keyStr = key.toString();
String valueStr = beanFactory.resolveEmbeddedValue(placeholderPrefix + keyStr.trim() + DEFAULT_PLACEHOLDER_SUFFIX);
propertiesMap.put(keyStr, valueStr);
}
}
/**
* This method gets the String value for a given String key for the property files.
*
* #param name - Key for which the value needs to be retrieved.
* #return Value
*/
public String getProperty(String name) {
return propertiesMap.get(name).toString();
}
This post also explatis howto access properties: http://maciej-miklas.blogspot.de/2013/07/spring-31-programmatic-access-to.html
You can access properties loaded by spring property-placeholder over such spring bean:
#Named
public class PropertiesAccessor {
private final AbstractBeanFactory beanFactory;
private final Map<String,String> cache = new ConcurrentHashMap<>();
#Inject
protected PropertiesAccessor(AbstractBeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
public String getProperty(String key) {
if(cache.containsKey(key)){
return cache.get(key);
}
String foundProp = null;
try {
foundProp = beanFactory.resolveEmbeddedValue("${" + key.trim() + "}");
cache.put(key,foundProp);
} catch (IllegalArgumentException ex) {
// ok - property was not found
}
return foundProp;
}
}
This is the finest way I got it to work:
package your.package;
import java.io.IOException;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PropertiesLoaderUtils;
public class ApplicationProperties {
private Properties properties;
public ApplicationProperties() {
// application.properties located at src/main/resource
Resource resource = new ClassPathResource("/application.properties");
try {
this.properties = PropertiesLoaderUtils.loadProperties(resource);
} catch (IOException ex) {
Logger.getLogger(ApplicationProperties.class.getName()).log(Level.SEVERE, null, ex);
}
}
public String getProperty(String propertyName) {
return this.properties.getProperty(propertyName);
}
}
create .properties file in classpath of your project and add path configuration in xml`<context:property-placeholder location="classpath*:/*.properties" />`
in servlet-context.xml after that u can directly use your file everywhere
Please use the below code in your spring configuration file to load the file from class path of your application
<context:property-placeholder
ignore-unresolvable="true" ignore-resource-not-found="false" location="classpath:property-file-name" />
I know this is an old thread, however, this topic in my opinion becomes of great importance for those using the functional approach for all those usecases where you need a microservice that loads "instantly" and therefore you avoid using annotations.
The problem that remained unsolved was to load eventually the environment variables which I had in my application.yml.
public class AppPropsLoader {
public static Properties load() {
var propPholderConfig = new PropertySourcesPlaceHolderConfigurer();
var yaml = new YamlPropertiesFactoryBean();
ClassPathResource resource = new ClassPathResource("application.yml");
Objects.requireNonNull(resource, "File application.yml does not exist");
yaml.setResources(resource);
Objects.requireNonNull(yaml.getObject(), "Configuration cannot be null");
propPholderConfig.postProcessBeanFactory(new DefaultListableBeanFactory());
propPholderConfig.setProperties(yaml.getObject());
PropertySources appliedPropertySources =
propPholderConfig.getAppliedPropertySources();
var resolver = new PropertySourcesPlaceholderResolver(appliedPropertySources);
Properties resolvedProps = new Properties();
for (Map.Entry<Object, Object> prop: yaml.getObject().entrySet()) {
resolvedProps.setProperty((String)prop.getKey(),
getPropertyValue(resolver.resolvePlaceHolders(prop.getValue()));
}
return resolvedProps;
}
static String getPropertyValue(Object prop) {
var val = String.valueOf(prop);
Pattern p = Pattern.compile("^(\\$\\{)([a-zA-Z0-9-._]+)(\\})$");
Matcher m = p.matcher(val);
if(m.matches()) {
return System.getEnv(m.group(2));
}
return val;
}
}

Resources