How to use environment variables in web.xml in spring? - java

I want to be able to reference environment variables in web.xml, something along the lines of this:
<context-param>
<param-name>log4jConfigLocation</param-name>
<param-value>classpath:conf/log4j-${SERVER_ENVIRONMENT}.properties</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.util.Log4j2ConfigListener</listener-class>
</listener>
<param-value>classpath:conf/log4j-​${SERVER_ENVIRONMENT}​.properties</param-value>
This question is similar to mine, and the most upvoted answer claims that:
If you are using Spring, you can create a bean and then directly use envvars or sysprops in Spring XML config files.
However, I don't understand how to do that. I've added this bean:
<context:property-placeholder />
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="serverEnvironment" value="${SERVER_ENVIRONMENT}"></property>
<property name="ignoreUnresolvablePlaceholders" value="true" />
</bean>
But I don't know if I'm doing it right, nor how to access that variable from web.xml. How should I define that bean, and how can I use its value in web.xml?

If you're willing to forego using XML configuration in favor of Java config it's quite easy to reference environment variables there. Just #Autowire Environment in your config class, then you can do something like
#Autowired
Environment environment;
#Bean
public String getServerEnvironment(){
return this.environment.getProperty("SERVER_ENVIRONMENT");
}
Then just call your bean and you're done. This assumes the property either exists in your environment or you've passed it in with -DSERVER_ENVIRONMENT at runtime.
Or you could just use Spring's #Profile annotation and make your logs environment dependent using that (several tutorials on that also).
Incidentally Spring Boot makes this process even easier.

Related

Referencing a value from a spring config

First a bit of setup info:
I have a multi-tenant spring based application. The multi-tenant enabling library is an in-house developed tool where I work that I have to use. How it works is that there is an interceptor that sets in front of the servlet for the application. Upon a request hitting the servlet it loads a tenant specific spring config for "stuff" needed for the tenant specified on the url hitting the servlet.
As stated, the above is just a bit of background. Now to the issue/question:
What I want to do is to create, in the tenant configuration that is loaded, a value that I can use to inject where I need. So, is there a way I can just define a constant in a spring config and then reference it via #Value or #Resource in java code?
There will be no bean implementation behind it, it would just be purely and only a key/value that I can reference where needed in my application by name. So, something to the effect of:
<bean name="MyIdentifier">
<property name="theId" value="1001" />
</bean>
And then can I do something like?
#Value{MyIdentifier.theId}
String theId;
And have Spring be aware of and inject the value. The problem is that doing something like above Spring complains there is no implementation for the bean. Notice, no class specified for the bean. The reason I want to do this is every tenant config file will contain this bean, but the actual value will vary per tenant.
Is there some other type to use in the config to do this? If so, what schemas have to be on the config?
I am guessing I am either trying to make Spring do something not intended, or, this is so simple I cannot see it since I have stared at it too long. Anyway, thanks for the help.
You can not create bean tag in configuration file without providing class implementation. If you want to inject the value of fields, you have to go for properties file instead.
Create property file as below:
application.properties
theId=1001
Load property file in your configuration:
<bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
<property name="ignoreUnresolvablePlaceholders" value="true"/>
<property name="locations">
<list>
<value>classpath:application.properties</value>
</list>
</property>
</bean>
And access the property in your concrete class:
#Value("${theId}")
String theId;

How does Spring start this app?

I'm parsing through some inherited code of a Java app that is deployed as a WAR file in JBOSS, and uses Spring, JMS, and HornetQ. I'm trying to figure out, for lack of a better phrase, what makes the app "go". Most of the Spring examples I've seen include an application with a main() method, which imperatively acts on the beans provided by the Spring context in some way. This application has no main(). As far as I can tell, this is what's happening:
The WAR file's web.xml uses a listener to launch Spring when the application starts in JBOSS, and passes it a config file
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:application-context.xml</param-value>
</context-param>
Spring then processes the application-context.xml file, which includes this snippet:
<bean id="jmsContainer"
class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory" />
<property name="destination" ref="destination" />
<property name="messageListener" ref="appListenerProxy" />
Through a couple more references in the application-context.xml, the "appListenerProxy" ultimately refers to a class in the application that implements SessionAwareMessageListener, which responds to messages on a queue.
The part that's tripping me up is that I don't see any kind of code to get the jmsContainer bean and do something with it. Is that a well-defined ID, such that the Spring framework is looking for it? And if so, is that documented somewhere (along with other IDs the framework might be looking for)? http://docs.spring.io/spring/docs/current/spring-framework-reference/html/jms.html seemed to be the closest I found, but it doesn't really specify whether that ID is just in the examples by convention, or if it's a meaningful string.
Thanks
Containers in spring are part of the framework core. The Framework will scan all the containers that implement a certain container interface, and initialize them.
This also relies on the Inversion of Control (IoC) principle.
For more information on IoC Container, check this page:
IoC Container
You don't really need to do anything with DefaultMessageListenerContainer bean, its frame work part.
The DefaultMessageListenerContainer bean establish JMS session with your JMS destination on application startup. When message are received in your destination it invokes the messageListener onMessage method- in your case appListenerProxy bean.
read here.

property placeholder in class attribute of a bean

A colleague of mine and I are playing around with the following spring configuration:
<beans>
<context:property-placeholder location='classpath:/configuration.properties'/>
<bean id="myBean" class="${type}" />
</beans>
We want to be able to provide a environment specific implementation of myBean. On a developers system the value of type would be a lightweight implementation of whatever myBean does. And in a production environment we would use a full-blown version.
When my colleague runs the code, everything works. When I run the code, I get a ClassNotFoundException, because spring tries to instantiate ${type}.class. And it is not like it sometimes works and sometimes does not. On my machine it always fails and on my colleagues machine it always works.
Does anybody knows what the problem is?
Thx in advance,
Yevgeniy
UPDATE
as requested, here is how we load the application context:
ClassPathXmlApplicationContext("application-configuration.xml");
the content of the properties file is pretty simple:
type=foobar.TestServiceImpl
Instead of trying to override the class with a placeholder, I would like to suggest an alternative approach for your problem. You could use the Profile functionality of Spring.
It would be simplier and safer to change the class depending of the environment.
<beans>
<beans profile="dev">
<bean id="myBean" class="dev.impl.MyBean" />
</beans
<beans profile="prod">
<bean id="myBean" class="prod.impl.MyBean" />
</beans
</beans>
You can then activate a given profile in development by adding the following system property to your server -Dspring.profiles.active="dev".
You can define a default profile which will be used by adding the following to your web.xml:
<context-param>
<param-name>spring.profiles.default</param-name>
<param-value>prod</param-value>
</context-param>
The following assumes Spring 3.1+.
I can tell you this much for sure. For Spring to fail with a ClassNotFoundException for class
${type}
means that it did not resolve the placeholder.
When you specify
<context:property-placeholder location='classpath:/configuration.properties'/>
Spring uses a PropertyPlaceholderBeanDefinitionParser to register either a PropertySourcesPlaceholderConfigurer or a PropertyPlaceholderConfigurer bean which will do the placeholder resolution.
PropertySourcesPlaceholderConfigurer is a BeanFactoryPostProcessor. This mean that it can modify bean definitions. Without going into much detail, if it cannot resolve a placeholder, the process fails with an IllegalArgumentException that states that the placeholder could not be resolved.
If you're saying that the ${type} wasn't resolved, then no PropertySourcesPlaceholderConfigurer or PropertyPlaceholderConfigurer beans were created. This probably means your context does not have
<context:property-placeholder location='classpath:/configuration.properties'/>
With the information you've shown us, that is what I think is going on. If you can prove otherwise, I'll ask you to provide a small reproducible example. Ideally, you would show the contents of your compiled project.
As far as I remember property placeholder fails mutually. This means that if it cannot locate file configuration.properties on your machine the property is just not initialized.
To approve this assumption try to "break" the application on your colleague's machine: change the location to something wrong, e.g. classpath:/configuration12345.properties. I believe that the problem will appear on his machine too.
Now, check what's wrong in your environment and why this file cannot be found there.
BUT: do you have something against profiles? Spring provides a cool feature that is attended exactly for your use-case: spring profiles.

Is there a way to pass the file location by jndi in a <util:properties /> tag?

I have the following properties declared in my spring-config.xml
<util:properties id="ldap" location="classpath:com/company/project/resources/some_configuration.properties"/>
Then I inject the values contained in the properties into some member variables using the spring #Value annotation in a service implementation (this approach is by far the cleanest/most elegant I have used in the implementation of the service and if possible I wouldn't want to change it).
The problem with this layout is that I have to modify the properties file and regenerate the application war for every deployment environment (quality, production, etc) and the server admins want to configure the some_configuration.properties path by JNDI (the application server is JBoss)
How can I pass the file location by jndi in the <util:properties /> tag?
Any help and suggestions would be appreciated
edit:
It would be nice if somebody comes out with a solution where I could do something like:
<util:properties id="ldap" location="jndi:url/some_configuration.properties"/>
Or similar
Old post, but this may be useful for others:
<jee:jndi-lookup id="ldapProps" jndi-name="your/jndi" resource-ref="true"/>
<util:properties id="ldap" location="file://#{ldapProps}/some_configuration.properties" />
I was looking something similar, this answer will help you using PropertyPlaceholderConfigurer: https://stackoverflow.com/a/3486315/439427.
HTH
In your case you will need to configure the PropertyPlaceholderConfigurer in your beans then you will just need to do the following change:
<util:properties id="ldap"
location="classpath:x/y/z/resources/${environment}.properties"/>
Where ${environment} will be set by an environment variable like this: -Denvironment=dev

Spring lazy initialization in development environment

In order to reduce the server startup time in development envrionment, I would like to change the default behaviour of Spring to lazily initialize the beans.
I know this can be done by specifying default-lazy-init="true" at the beans level. However I would not want to change this property everytime I get the latest config files from SCM and change it back before checking it back in.
Is there any other way out to externalize this property? Like specifying a System property?
I also tried to define a property in an environment specific property file and refer to the property in beans element, but it did not work.
default-lazy-init="${default-lazy-init-value}"
Any other way this can be achieved easily?
How about taking default-lazy-init in an external properties file and passing it to the bean definition
XML
<bean id="propertyPlaceholderConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:system-env.properties</value>
</list>
</property>
</bean>
<bean id="bean1" class="com.Foo" lazy="${default-lazy-init}"/>
Properties File (system-env.properties)
#set true in dev (if needed)
default-lazy-init=true
You could use the following:
<beans default-lazy-init="true">
<!-- no beans will be pre-instantiated... -->
</beans>
...as described on http://static.springsource.org/spring/docs/3.1.x/spring-framework-reference/html/beans.html#beans-factory-lazy-init

Categories