Adding system properties in Spring MVC webapp - java

I am using Hibernate 4.3.7.Final and Log4j2 in my Spring MVC webapp, published via Tomcat 7. All configuration is done via JavaConfig (i.e. there is no web.xml or other XML config files).
By default the Hibernate logging does not go through Log4j, for reasons explained in the Apache wiki. In order to resolve this I need to create a system setting as follows:
System.setProperty("org.jboss.logging.provider", "slf4j");
As my application is a webapp there is no Main thread, and as a result I an unsure where to put this System.setProperty call. Any advice would be appreciated.

You could define this system property in context listener which is the first entry point as below:
#WebListener
public class ContextListenerExample implements ServletContextListener {
public void contextInitialized(ServletContextEvent e){
System.setProperty("org.jboss.logging.provider", "slf4j");
}
}
You could even define system property using spring as below:
<bean id="setupJBossLoggingProperty"
class="org.springframework.batch.support.SystemPropertyInitializer"
p:keyName="org.jboss.logging.provider" p:defaultValue="slf4j"/>
And then you could say something like:
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
depends-on="setupJBossLoggingProperty">
...
So this means system property will be setted first and then hibernate bean is going to be initialised.

If you are using some WebApplicationInitializer implementation to bootstrap your Spring application (which I assume you are since you have no web.xml) you could put it in onStartup() method like this:
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
System.setProperty("org.jboss.logging.provider", "slf4j");
}

Related

Injecting beans and values (as defined in context.xml) into servlet

I'm trying to inject a Spring-managed bean and a string value into a servlet. The servlet is defined in context.xml as following:
<bean name="gwtlogging" class="com.somepackage.MyRemoteLogger">
<property name="symbolMapsDirectory" value="/WEB-INF/deploy/gwt/symbolMaps/"/>
<property name="serializationPolicyResolver" ref="serializationPolicyResolver"/>
</bean>
I came across using this method:
#Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
SpringBeanAutowiringSupport.processInjectionBasedOnServletContext(this,
config.getServletContext());
}
This works, but has some disadvantages:
I want to inject beans as defined in context.xml and not per autowiring
I need to inject a string value (not a bean), which seems impossible this way
Ideally I want that all values defined in xml will be somehow injected into servlet instance.
If it's available to you, you can declare a Servlet bean in your XML configuration file, use Spring's WebApplicationInitializer (or write your own ServletContainerInitializer) to load the XML file in a WebApplicationContext, retrieve the Servlet bean, and add it to the ServletContext with add(String, Servlet).
The Servlet instance will have been completely prepared through Spring and will be used by your Servlet container.

How to make a Spring Application' Business Logic User Configurable?

I am a developer working on a Java web application that is built on the Spring framework.
This application will be deployed to several different customers.
There is a class which contains some business logic that is different for each client.
From a Spring framework point of view, it is enough to simply wire in the appropriate class (as a bean) for each client.
However, the developers are not responsible for deployment of the application. The Operations team is responsible, and having them open up the WAR file and modify the spring configuration XML for each client deployment is probably too much to ask from them. Properties files are ok, but modifying internal files - probably not.
Has anyone else come up with a strategy for dealing with this?
Edit:
To give an example of what I'm talking about:
public interface IEngine {
void makeNoise();
}
public class Car {
public void setEngine(IEngine engine) {
this.engine = engine;
}
}
Customer A's business logic:
HeavyDutyEngine implements IEngine {
public void makeNoise() {
System.out.println("VROOOM!");
}
}
Customer B's business logic:
LightWeightEngine implements IEngine {
public void makeNoise() {
System.out.println("putputput");
}
}
In the Spring configuration XML:
For client A, it might look like this:
<bean id="hdEngine" class="HeavyDutyEngine" />
<bean id="lwEngine" class="LightWeightEngine" />
<bean id="car" class="Car">
<property name="engine" ref="hdDngine">
</bean>
For client B, it might look like this:
<bean id="hdEngine" class="HeavyDutyEngine" />
<bean id="lwEngine" class="LightWeightEngine" />
<bean id="car" class="Car">
<property name="engine" ref="lwEngine">
</bean>
To configure Spring for different environments, you can use the concept called spring "profiles". (introduced in Spring 3.1)
You have different ways so enable/disable this properties. For example java properties parameter.
But because you are using a WAR, and therefore some Servlet container, I would recommend to put this configuration in the Servlet container.
In a tomcat for example, you can put this line in the context.xml (global or application specific) to enable a profile
<Parameter name="spring.profiles.active" value="myProfile"/>
You can move some configuration to DB, if you are using one.
And you can use something like this
ref="{ref-name}"
where ref-name can be resolved using properties file(default) by configuring PropertyPlaceholderConfigurer.
Or you can write your own wrapper over PropertyPlaceholderConfigurer which will take the values from DB table which is external to you deployable WAR file.
In one of mine project, we used this method to resolve custom dependencies. The wrapper which looks up the DB used to take first priority and if the DB doesn't have the key/value pair, then properties file (bundled in the WAR) was used to resolve dependencies.
This will also allow you to change some value externally from DB, however with IBM Websphere we need to recycle the server for changes to take place.

Injecting spring bean in Hazelcast entry listener

I am using Hazelcast 2.6 with Spring. Currently I have entry listener configured using spring-hazelcast configuration. For method entryEvicted I want to call method of my spring bean. Is it possible to inject that bean via xml configuration (or annotation) where is my entry listener configured.
Here is sample code of my entry listener.
public class HazelcastSessionMapEntryListener implements EntryListener<String,SessionMapEntry>{
private CustomBean customBean;
#Override
public void entryEvicted(EntryEvent<String, SessionMapEntry> event) {
customBean.method(event);
}....}
I am wondering is it possible to have instance of customBean injected without calling application context getBean method from my code.
In Hazelcast you can configure a spring bean as a listener and configure that bean how ever you like. Here is a sample for your case;
<hz:listeners>
<hz:listener implementation="entryListener"/>
</hz:listeners>
<bean id="entryListener" class="com.acme.EntryListener">
<property name="customBean" ref="customBean" />
</bean>
<bean name="customBean" class="com.acme.CustomBean"/>

How do I import in a spring xml conditionally [duplicate]

What I would like to achieve is the ability to "dynamically" (i.e. based on a property defined in a configuration file) enable/disable the importing of a child Spring XML context.
I imagine something like:
<import condition="some.property.name" resource="some-context.xml"/>
Where the property is resolved (to a boolean) and when true the context is imported, otherwise it isn't.
Some of my research so far:
Writing a custom NamespaceHandler (and related classes) so I can register my own custom element in my own namespace. For example: <myns:import condition="some.property.name" resource="some-context.xml"/>
The problem with this approach is that I do not want to replicate the entire resource importing logic from Spring and it isn't obvious to me what I need to delegate to to do this.
Overriding DefaultBeanDefinitionDocumentReader to extend the behaviour of the "import" element parsing and interpretation (which happens there in the importBeanDefinitionResource method). However I'm not sure where I can register this extension.
Prior to Spring 4, the closest you can get using standard Spring components is:
<import resource="Whatever-${yyzzy}.xml"/>
where ${xyzzy} interpolates a property from the system properties. (I use a hacky custom version of the context loader class that adds properties from other places to the system properties object before starting the loading process.)
But you can also get away with importing lots of unnecessary stuff ... and use various tricks to only cause the necessary beans to be instantiated. These tricks include:
placeholder and property substitution
selecting different beans using the new Spring expression language,
bean aliases with placeholders in the target name,
lazy bean initialization, and
smart bean factories.
This is now completely possible, using Spring 4.
In your main application content file
<bean class="com.example.MyConditionalConfiguration"/>
And the MyConditionalConfiguration looks like
#Configuration
#Conditional(MyConditionalConfiguration.Condition.class)
#ImportResource("/com/example/context-fragment.xml")
public class MyConditionalConfiguration {
static class Condition implements ConfigurationCondition {
#Override
public ConfigurationPhase getConfigurationPhase() {
return ConfigurationPhase.PARSE_CONFIGURATION;
}
#Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// only load context-fragment.xml if the system property is defined
return System.getProperty("com.example.context-fragment") != null;
}
}
}
And then finally, you put the bean definitions you want included in the /com/example/context-fragment.xml
See the JavaDoc for #Conditional
As mentioned earlier, this can be easily accomplished with profiles if you're using Spring 3.1+
<!-- default configuration - will be loaded if no profile is specified -->
<!-- This will only work if it's put at the end of the configuration file -->
<!-- so no bean definitions after that -->
<beans profile="default">
<import resource="classpath:default.xml" />
</beans>
<!-- some other profile -->
<beans profile="otherProfile">
<import resource="classpath:other-profile.xml" />
</beans>
otherProfile can be easily activated with e.g.
mvn install -Dspring.profiles.active=otherProfile
if you're using different profiles in tests, just add -DforkMode=never to make sure that the tests will run inside same VM, therefore the param spring.profiles.active wont be lost
With Spring 3.1.x you can use bean profiles to achieve conditional resource import and bean instantiation. This is of course of no help if you are using an earlier version :)
For the record, Robert Maldon explains how to accomplish conditional definition of beans in this post: http://robertmaldon.blogspot.com/2007/04/conditionally-defining-spring-beans.html. It is a bit long to copy it here (besides, I don't think I should copy-paste his article anyway).
The end result with this approach, adapted for your example, is:
<condbean:cond test="${some.property.name}">
<import resource="some-context.xml"/>
</condbean:cond>
It is certainly not so simple as Stephen C's solution, but it is much more poweful.
Another one to consider for Spring 3.0:
<alias name="Whatever" alias=""Whatever-${yyzzy}" />
where ${xyzzy} interpolates a property from the system properties.
Another option is to have your app load a modules-config.xml file that is located in the /conf folder and edit it during the install/config phase to uncomment the modules you want loaded.
This is the solution I'm using with a web application that serves as a container for different integration modules. The web application is distributed with all the different integration modules. A modules-config.xml is placed in tomcat's /conf folder and the conf folder is added to the classpath (via catalina.properties/common.loader property). My web app webapp-config.xml has a <import resource="classpath:/modules-config.xml"/> to get it loaded.
You can override contextInitialized(javax.servlet.ServletContextEvent event) in your own ContextLoaderListener and set required System property before super.contextInitialized(event) called like this
package com.mypackage;
import org.springframework.web.context.ContextLoaderListener;
public class MyContextLoaderListener extends ContextLoaderListener {
public void contextInitialized(javax.servlet.ServletContextEvent event) {
System.setProperty("xyz", "import-file-name.xml");
super.contextInitialized(event);
}
}
And than replace ContextLoaderListener to MyContextLoaderListener in your web.xml
<listener>
<listener-class>com.mypackage.MyContextLoaderListener</listener-class>
</listener>
Now you can use in your spring.xml
<import resource="${xyz}" />
I hope this will help.

Dynamically load a spring xml file based on database values

We currently have a Spring web application and are doing our configuration using XML files. We are starting Spring the DispatcherServlet which creates an XmlWebApplicationContext and loads it from the default location: spring-servlet.xml.
I am specifying several additional configuration files using the context-param contextConfigLocation. This loads up our entire application from the XML files.
So here's what I want to do. The XML file contains the database connection information and our DAOs for accessing these tables. I want to use one of those DAOs to read a value from the database and load an additional set of beans from the XML file.
So if the database value retrieved is orange, I want to load beans from orange.xml. If it's apple, I want to load apple.xml. I want these beans to be part of the same application context so after they're loaded, I can move forward without noticing the difference.
I'm wondering if I should implement my own sub-class of XmlWebApplicationContext and have DispatcherServlet implement that, but I'm not quite sure how to proceed with that.
Not exactly loading from the different files, but you can try to use Spring Environment and Profile abstractions.
<beans profile="apple">
<bean id="someBean">
...first set of bean parameters...
</bean>
</beans>
<beans profile="orange">
<bean id="someBean">
...second set of bean parameters...
</bean>
</beans>
And in java:
context.getEnvironment().setActiveProfiles("orange");
context.refresh();
You could use a BeanFactoryPostProcessor to load the configuration.
For example, if you have a LocationService that give the config locations as String[]:
public class XmlBeanDefinitionReaderPostProcessor implements BeanFactoryPostProcessor {
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader((BeanDefinitionRegistry) beanFactory);
ResourceLoader resourceLoader = new DefaultResourceLoader();
reader.setResourceLoader(new DefaultResourceLoader());
reader.setEntityResolver(new ResourceEntityResolver(resourceLoader));
reader.setEnvironment(new StandardEnvironment());
LocationService locationService = (LocationService) beanFactory.getBean("locationService");
reader.loadBeanDefinitions(locationService.getLocations());
}
}
Is not exactly the same as the reader is unaware of the already loaded beans and could be aliases or bean names colisions.
Note that your LocationService should not use Autorwire, AOP Transactional Proxies, and something that in general implies the use of BeanPostProcessors.
Other option to reuse the same XmlBeanDefinitionReader is overriding postProcessBeanFactory method in XmlWebApplicationContext:
public class CustomWebApplicationContext extends XmlWebApplicationContext {
private XmlBeanDefinitionReader reader;
#Override
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
this.reader = reader;
super.loadBeanDefinitions(reader);
}
#Override
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
LocationService locationService = (LocationService) beanFactory.getBean("locationService");
this.reader.loadBeanDefinitions(locationService.getLocations());
super.postProcessBeanFactory(beanFactory);
}
}
We ended up extending XmlWebApplicationContext and overriding the loadBeans method. We load the beans, look up the bean that provides our configuration, then switch profiles and run again with the new profiles.
Thanks for all the help.

Categories

Resources