Maven plugin to validate Spring configuration? - spring

Does anyone know of a Maven plugin that can be used to validate Spring configuration files? By validation, I mean:
Verify all beans reference a class on the build path
Verify all bean references refer to a valid bean definition
Verify no orphaned beans exist
Other configuration mistakes I'm sure I'm missing.
I searched around and didn't come up with anything.
A Maven plugin would be ideal for my purposes, but any other tools (Eclipse plugin, etc.) would be appreciated.

What we do on our project is simply write a JUnit test which loads the Spring configuration. This does a few of the things you described like:
Validate the XML
Ensures beans can be loaded with classes on the classpath (at least beans which aren't lazy-loaded)
It does not check that there are no orphan beans. There is no reliable way of doing this anyway considering from anywhere in your code, you can lookup beans directly given their ID. Just because a bean is not referenced by any other beans does not mean it is not used. In fact all Spring configs will have at least one bean which is not referenced by other beans because there always has to be a root to the hierarchy.
If you have beans which rely on real services like databases or something and you don't want to connect to these services in a JUnit test, you simply need to abstract the configuration to allow for test values. This can be easily accomplished with something like the PropertyPlaceholderConfigurer which allows you to have different properties specified in separate config files for each environment and then referenced by one beans definition file.
EDIT (to include sample code):
The way we do this is have at least 3 different spring files...
src/main/resources/applicationContext.xml
src/main/resources/beanDefinitions.xml
src/test/resources/testContext.xml
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<import resource="classpath:beanDefinitions.xml"/>
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="file:path/environment.properties" />
</bean>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${driver}" />
...
</bean>
... <!-- more beans which shouldn't be loaded in a test go here -->
</beans>
beanDefinitions.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="myBean" class="com.example.MyClass">
...
</bean>
<bean id="myRepo" class="com.example.MyRepository">
<property name="dataSource" ref="dataSource"/>
...
</bean>
... <!-- more beans which should be loaded in a test -->
</beans>
testContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<import resource="classpath:beanDefinitions.xml"/>
<bean id="dataSource" class="org.mockito.Mockito" factory-method="mock">
<constructor-arg value="org.springframework.jdbc.datasource.DriverManagerDataSource"/>
</bean>
</beans>
There are many things going on here, let me explain...
The applicationContext.xml file is the main spring file for your whole application. It contains an PropertyPlaceHolder bean to allow certain property values to be configurable between different environments we deploy to (test vs. prod). It imports all of the main beans that the app needs to run. Any beans which should not be used in a test, like DB beans, or other classes which communicate with external services/resources should be definied in this file.
The beanDefinitions.xml file has all of your normal beans in it which don't rely on external things. These beans can and will reference beans defined in the appContext.xml file.
The testContext.xml file is the test version of the appContext. It needs versions of all beans defined in the appContext.xml file but we used a mocking library to instantiate these beans. This way the real classes aren't used and there is no risk of access external resources. This file also doesn't need the property placeholder bean.
Now that we have a test context which we aren't afraid to load from a test, here is the code to do it...
SpringContextTest.java
package com.example;
import org.junit.Test;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;
public class SpringContextTest {
#Test
public void springContextCanLoad() {
XmlBeanFactory factory = new XmlBeanFactory(new ClassPathResource("testContext.xml"));
for (String beanName : factory.getBeanDefinitionNames()) {
Object bean = factory.getBean(beanName);
// assert anything you want
}
}
}
This may not be the optimal way of doing it; the ApplicationContext class is the recommended way of loading spring contexts. The above might be able to be replaced by:
#Test
public void springContextCanLoad() {
ApplicationContext context = new FileSystemXmlApplicationContext("classpath:testContext.xml");
}
I believe that one line will accomplish everything you need to verify your spring context is wired correctly. From there, you can load beans and assert like before.
Hope this helps!

Here's the URL of Spring IDE update site (Eclipse plugin). It does what you described above. Their site seems to be unavailable.

I came across this question when googling - I had exactly the same question.
I've written a (very much untested) Maven plugin to do this this. It currently only supports WARs but could easily be extended. In addition, I don't bother actually loading the beans since I don't want the hassle of having to maintain a large set of properties just to satisfy this plugin.
Here it is if it's ever any use:
package myplugins;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.project.MavenProject;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConstructorArgumentValues;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.core.io.FileSystemResource;
import org.springframework.util.ClassUtils;
import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* Validates Spring configuration resource and class references
* using a classloader that looks at the specified WAR's lib and classes
* directory.
* <p/>
* It doesn't attempt to load the application context as to avoid the
* need to supply property files
* <br/>
* TODO: maybe one day supplying properties will become an optional part of the validation.
*
* #goal validate
* #aggregator
* #phase install
*/
public class WarSpringValidationMojo extends AbstractMojo
{
private final static String FILE_SEPARATOR = System.getProperty("file.separator");
/**
* Project.
* #parameter expression="${project}"
* #readonly
*/
private MavenProject project;
/**
* The WAR's root Spring configuration file name.
*
* #parameter expression="${applicationContext}" default-value="webAppConfig.xml"
*/
private String applicationContext;
/**
* The WAR's directory.
*
* #parameter expression="${warSourceDirectory}" default-value="${basedir}/target/${project.build.finalName}"
*/
private File warSourceDirectory;
#SuppressWarnings("unchecked")
public void execute() throws MojoExecutionException
{
try
{
if ("war".equals(project.getArtifact().getType()))
{
File applicationContextFile = new File(warSourceDirectory, "WEB-INF" + FILE_SEPARATOR + applicationContext);
File classesDir = new File(warSourceDirectory, "WEB-INF" + FILE_SEPARATOR + "classes");
File libDir = new File(warSourceDirectory, "WEB-INF" + FILE_SEPARATOR + "lib");
Set<URL> classUrls = new HashSet<URL>();
if (classesDir.exists())
{
classUrls.addAll(getUrlsForExtension(classesDir, "class", "properties"));
}
if (libDir.exists())
{
classUrls.addAll(getUrlsForExtension(libDir, "jar", "zip"));
}
ClassLoader parentClassLoader = Thread.currentThread().getContextClassLoader();
ClassLoader classLoader = new URLClassLoader(classUrls.toArray(new URL[classUrls.size()]), parentClassLoader);
ClassUtils.overrideThreadContextClassLoader(classLoader);
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
factory.setBeanClassLoader(classLoader);
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
reader.setValidating(true);
reader.loadBeanDefinitions(new FileSystemResource(applicationContextFile));
for (String beanName : factory.getBeanDefinitionNames())
{
validateBeanDefinition(classLoader, factory.getBeanDefinition(beanName), beanName);
}
getLog().info("Successfully validated Spring configuration (NOTE: validation only checks classes, " +
"property setter methods and resource references)");
}
else
{
getLog().info("Skipping validation since project artifact is not a WAR");
}
}
catch (Exception e)
{
getLog().error("Loading Spring beans threw an exception", e);
throw new MojoExecutionException("Failed to validate Spring configuration");
}
}
private void validateBeanDefinition(ClassLoader beanClassloader, BeanDefinition beanDefinition, String beanName) throws Exception
{
Class<?> beanClass = validateBeanClass(beanClassloader, beanDefinition, beanName);
validateBeanConstructor(beanDefinition, beanName, beanClass);
validateBeanSetters(beanDefinition, beanName, beanClass);
}
private Class<?> validateBeanClass(ClassLoader beanClassloader, BeanDefinition beanDefinition, String beanName) throws Exception
{
Class<?> beanClass;
try
{
beanClass = beanClassloader.loadClass(beanDefinition.getBeanClassName());
}
catch (ClassNotFoundException e)
{
throw new ClassNotFoundException("Cannot find " + beanDefinition.getBeanClassName() +
" for bean '" + beanName + "' in " + beanDefinition.getResourceDescription(), e);
}
return beanClass;
}
private void validateBeanConstructor(BeanDefinition beanDefinition, String beanName,
Class<?> beanClass) throws Exception
{
boolean foundConstructor = false;
ConstructorArgumentValues constructorArgs = beanDefinition.getConstructorArgumentValues();
Class<?>[] argTypes = null;
if (constructorArgs != null)
{
Constructor<?>[] constructors = beanClass.getDeclaredConstructors();
int suppliedArgCount = constructorArgs.getArgumentCount();
boolean isGenericArgs = !constructorArgs.getGenericArgumentValues().isEmpty();
for (int k = 0; k < constructors.length && !foundConstructor; k++)
{
Constructor<?> c = constructors[k];
knownConstructorLoop:
{
Class<?>[] knownConstructorsArgTypes = c.getParameterTypes();
if (knownConstructorsArgTypes.length == suppliedArgCount)
{
if (isGenericArgs)
{
foundConstructor = true; // TODO - support generic arg checking
}
else
{
for (int i = 0; i < knownConstructorsArgTypes.length; i++)
{
Class<?> argType = knownConstructorsArgTypes[i];
ConstructorArgumentValues.ValueHolder valHolder = constructorArgs.getArgumentValue(i,
argType);
if (valHolder == null)
{
break knownConstructorLoop;
}
}
foundConstructor = true;
}
}
}
}
}
else
{
try
{
Constructor c = beanClass.getConstructor(argTypes);
foundConstructor = true;
}
catch (Exception ignored) { }
}
if (!foundConstructor)
{
throw new NoSuchMethodException("No matching constructor could be found for bean '" +
beanName + "' for " + beanClass.toString() + " in " + beanDefinition.getResourceDescription());
}
}
private void validateBeanSetters(BeanDefinition beanDefinition, String beanName, Class<?> beanClass) throws Exception
{
MutablePropertyValues properties = beanDefinition.getPropertyValues();
List<PropertyValue> propList = properties.getPropertyValueList();
try
{
Method[] methods = beanClass.getMethods();
for (PropertyValue p : propList)
{
boolean foundMethod = false;
String propName = p.getName();
String setterMethodName = "set" + propName.substring(0, 1).toUpperCase();
if (propName.length() > 1)
{
setterMethodName += propName.substring(1);
}
for (int i = 0; i < methods.length && !foundMethod; i++)
{
Method m = methods[i];
foundMethod = m.getName().equals(setterMethodName);
}
if (!foundMethod)
{
throw new NoSuchMethodException("No matching setter method " + setterMethodName
+ " could be found for bean '" + beanName + "' for " + beanClass.toString() +
" in " + beanDefinition.getResourceDescription());
}
}
}
catch (NoClassDefFoundError e)
{
getLog().warn("Could not validate setter methods for bean " + beanName +
" since getting the methods of " + beanClass + " threw a NoClassDefFoundError: "
+ e.getLocalizedMessage());
}
}
private Collection<? extends URL> getUrlsForExtension(File file, String... extensions) throws Exception
{
Set<URL> ret = new HashSet<URL>();
if (file.isDirectory())
{
for (File childFile : file.listFiles())
{
ret.addAll(getUrlsForExtension(childFile, extensions));
}
}
else
{
for (String ex : extensions)
{
if (file.getName().endsWith("." + ex))
{
ret.add(file.toURI().toURL());
break;
}
}
}
return ret;
}
}
And the plugin's pom.xml:
<?xml version="1.0"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
... <my project's parent> ...
</parent>
<groupId>myplugins</groupId>
<artifactId>maven-spring-validation-plugin</artifactId>
<version>1.0</version>
<packaging>maven-plugin</packaging>
<name>Maven Spring Validation Plugin</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-plugin-api</artifactId>
<version>2.0</version>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-project</artifactId>
<version>2.0.8</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>3.0.7.RELEASE</version>
</dependency>
</dependencies>
</project>
Once installed, run like so at the root level of your WAR module:
mvn myplugins:maven-spring-validation-plugin:validate

Related

Setter injection in Spring

I am Running Spring Core program and I got this Error. and I configure build path and add all jar files.
structure of project
WishMessageGenerator.java
package com.nt.beans;
import java.util.Date;
public class WishMessageGenerator {
private Date date;
public WishMessageGenerator() {
System.out.println("WishMessageGenerator:0-param constructor");
}
public void setDate(Date date) {
System.out.println("WishMessageGenerator:setDate(-)");
this.date = date;
}
public String generateMessage(String user) {
int hour = 0;
System.out.println("Injected date::"+date);
hour = date.getHours();
if(hour < 12)
return "Good Morning :::"+user;
else if(hour < 16)
return "Good AfterNoon :::"+user;
else if(hour < 20)
return "Good Evening :::"+user;
else
return "Good Night"+user;
}
}
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- Dependent Bean class cfg -->
<bean id="dt" class="java.util.Date" />
<!-- Target Bean class cfg -->
<bean id="wmg" class="com.nt.beans.WishMessageGenerator">
<property name="date" ref="dt"></property><!-- Setter injection -->
</bean>
</beans>
SetterInection.java
package com.nt.test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import com.nt.beans.WishMessageGenerator;
public class SetterInjection {
public static void main(String[] args) {
Resource res = null;
BeanFactory factory = null;
Object obj = null;
WishMessageGenerator generator = null;
res = new FileSystemResource("src/com/nt/cfgs/applicationContext.xml");
factory = new XmlBeanFactory(res);
obj = factory.getBean("wmg");
generator = (WishMessageGenerator)obj;
System.out.println("result:::"+generator.generateMessage("KingRk"));
}
}
I got this output:
Error: Unable to initialize main class com.nt.test.SetterInjection
Caused by: java.lang.NoClassDefFoundError: org/springframework/core/io/Resource
The output is:
we have to add jar into class path not module path
Add jar into class path using Build path
Buid path

SpringBoot and use / load a dynamic "applicationContext.xml" instead of hard coded one

I've seen a hundred examples of this:
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ImportResource;
#SpringBootApplication
#ImportResource("classpath:applicationContext.xml")
public class MySpringBootApplication {
public static void main(String[] args) {
SpringApplication.run(MySpringBootApplication.class, args);
}
}
And I have been on a rabbit trail for many hours now.
I am building a framework....and I need load (a handful of dependencies, not all of them...) from the xml dependency injection file (aka, "beans") :
applicationContext.xml
and I need to name to be dynamic, not hard coded.
String myValue = "DefaultEnvVarValue";
String envValue = System.getenv("MYENVVARIABLENAME");
if (null != envValue )
{
myValue=envValue;
}
String topLevelAppContextFileName = "applicationContext." + myValue + ".xml";
Without springboot, I would do this:
ApplicationContext context = new ClassPathXmlApplicationContext(topLevelAppContextFileName);
Is there a way to pull this off with SpringBoot?
I found PropertySourcesPlaceholderConfigurer for property files, but cannot find anything for the dependency injection.
Sidenote:
Before I get a "xml bad" comment, most of my dependencies are annotation based. But I'm making a framework for others to use, and therefore I need a handful of them to be xml-driven.....aka, I have a legit reason to have some of the DI be xml driven.
This could work -
Config
public class DemoApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
#Override
public void initialize(ConfigurableApplicationContext ac) {
ac = new ClassPathXmlApplicationContext(topLevelAppContextFileName);
}
}
Main
public static void main(String args[]) {
new SpringApplicationBuilder(Application.class)
.initializers(new DemoApplicationContextInitializer())
.run(
}
For future readers, I ended up doing this:
#SpringBootApplication
#ImportResource({"classpath*:applicationContext.xml"})
public class MySpringBootApplication {
public static void main(String[] args) {
try {
URL resource = MySpringBootApplication.class.getResource("/applicationContext.xml");
if (null == resource || StringUtils.isBlank(resource.getPath())) {
throw new FileNotFoundException("applicationContext.xml not found. The entry dependency injection file must be applicationContext.xml");
}
org.springframework.context.ConfigurableApplicationContext applicationContext = SpringApplication.run(MySpringBootApplication.class, args);
And then I put the "dynamic" part in the inside applicationContext.xml file.
Note the ":" delimiter that will allow a default value if the environment variable does not exist.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd">
<import resource="projectContext.${MYENVVARIABLENAME:DefaultEnvVarValue}.xml"/>
That was simpler to implement, even though I technically have 2 files, instead of one.
So if the environment variable does not exist, it will default to importing the second file called:
projectContext.DefaultEnvVarValue.xml

How to parse Spring Integration HTTP gateway response into a Bean?

I've been trying to get a simple integration workflow for practice purposes. The thing is I just started working with Spring and I'm having a hard time understanding Integration and how it works.
Right now I've got a really simple backend app with Spring MVC that returns
[
{"id":1,"name":"Series test","synopsis":"Testing reading items from JSON.","imageUrl":"http://some.where/images/some_image.png"},
{"id":2,"name":"Arrow","synopsis":"Some guy in a hood shooting arrows to some guys with superpowers.","imageUrl":"http://some.where/images/some_image.png"},
{"id":3,"name":"Primeval","synopsis":"Some guys with guns killing dinosaurs and lots of infidelity.","imageUrl":"http://some.where/images/some_image.png"},
{"id":4,"name":"Dr. Who","synopsis":"It's bigger on the inside.","imageUrl":"http://some.where/images/some_image.png"},
{"id":5,"name":"Fringe","synopsis":"Weird things happen.","imageUrl":"http://some.where/images/some_image.png"},
{"id":6,"name":"Monster Hunter Freedom Unite","synopsis":"Wait. This is a game.","imageUrl":"http://some.where/images/some_image.png"}
]
to http://localhost:9000/api/series/findAll and a runnable Spring project that, with Integration, attempts to recover that data and convert it to a Series (bean with the same properties as the JSON) array.
If I don't add a reply-channel to the outbound-gateway everything works just fine. But when I send it to another channel to parse it into a Series object I start getting "Dispatcher has no subscribers" on the new channel. It makes sense but it leaves me not knowing how to proceed now.
My project files, apart from Series, look like this right now:
Startup.java
package com.txus.integration;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.List;
import java.util.concurrent.Future;
public class Startup {
#Autowired
RequestGateway requestGateway;
public static void main(String[] args) throws Exception {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("/META-INF/spring/integration-components.xml");
RequestGateway requestGateway = context.getBean(RequestGateway.class);
Future<String> promise = requestGateway.getSeries("");
while (!promise.isDone()) {
Thread.sleep(1000);
}
String response = promise.get();
printReadable(response);
context.close();
System.exit(0);
}
public static void printReadable(String string) {
String separator = "= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =";
System.out.println("\n" + separator + "\n" + string + "\n" + separator + "\n");
}
public static void printReadable(List<String> strings) {
for (String string : strings) printReadable(string);
}
}
RequestGateway
package com.txus.integration;
import com.txus.entities.Series;
import org.springframework.integration.annotation.Gateway;
import java.util.concurrent.Future;
public interface RequestGateway {
#Gateway(requestChannel="responseChannel")
Future<String> getSeries(String jsonString);
#Gateway(requestChannel="responseChannel")
Future<String> getSeries(Series series);
}
integration-components.xml
<beans:beans
xmlns="http://www.springframework.org/schema/integration"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:http="http://www.springframework.org/schema/integration/http"
xmlns:task="http://www.springframework.org/schema/task"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/integration/http http://www.springframework.org/schema/integration/http/spring-integration-http.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd">
<context:annotation-config/>
<context:component-scan base-package="com.txus"/>
<!-- START: Spring Integration -->
<!-- Integration: Channels -->
<channel id="requestChannel"/>
<channel id="responseChannel"/>
<channel id="failedChannel"/>
<!-- Integration: Loggers -->
<logging-channel-adapter
id="payloadLogger" level="DEBUG" expression="'### Message [' + headers.id + '] payload: ' + payload"/>
<logging-channel-adapter
id="headersLogger" level="DEBUG" expression="'### Message [' + headers.id + '] headers: ' + headers"/>
<!-- Integration: Flow -->
<gateway
service-interface="com.txus.integration.RequestGateway"
default-request-timeout="5000" async-executor="executor">
<method name="getSeries" request-channel="inputChannel"/>
</gateway>
<task:executor id="executor" pool-size="100"/>
<payload-type-router input-channel="inputChannel" default-output-channel="failedChannel">
<mapping type="java.lang.String" channel="requestChannel"/>
<mapping type="com.txus.entities.Series" channel="objectToJSONChannel"/>
</payload-type-router>
<object-to-json-transformer
id="objectToJsonTransformer" input-channel="objectToJSONChannel" output-channel="requestChannel"/>
<http:outbound-gateway
http-method="GET"
expected-response-type="java.lang.String"
url="http://localhost:9000/api/series/findAll"
request-channel="requestChannel"
reply-channel="jsonToSeries"
reply-timeout="30000"/>
<map-to-object-transformer
input-channel="jsonToSeries" output-channel="responseChannel" type="com.txus.entities.Series"/>
<!-- END: Spring Integration -->
</beans:beans>
You have an issue with the last component where you configure output-channel="responseChannel". There is just nothing which is subscribed to that channel.
I see your #Gateway config on the matter, but it is a bit wrong. XML configuration has a precedence over annotations there. Hence the requestChannel in the end is exactly inputChannel.
If you'd like to send the result of <map-to-object-transformer> to the responseChannel and accept it as a return from the RequestGateway invocation, you should specify that responseChannel as a reply-channel on the gateway configuration.
From other side you just don't need it there and the TemporaryReplyChannel comes to the rescue.
Please, refer for more information to the Spring Integration Manual.

Spring Null Pointer Exception [duplicate]

This question already has answers here:
Why is my Spring #Autowired field null?
(21 answers)
Closed 8 years ago.
I've got a NullPointerException that is bugging the hell out of me. In my cursory research on SO I've found that this is usually happening when people are not autowiring their jdbctemplate, but as far as I can tell that should be wiring correctly. As a heads up I'm still learning the basics of Spring, and the code I'm working with is part of a legacy project.
ReportDaoImpl
#Service
public class ReportDaoImpl implements ReportDao {
#Autowired JdbcTemplate jdbcTemplate;
private static final Logger log = Logger.getLogger(ReportDaoImpl.class);
private static final String SELECT_ALL_ACCOUNT_INFO = "SELECT acct_name, login_name, pswd FROM PG_PAYPAL_ACCOUNTS";
#Autowired
public ReportDaoImpl(DataSource dataSource)
{
log.debug("attempt building");
jdbcTemplate = new JdbcTemplate(dataSource);
log.debug("building complete");
}
#Override
public ArrayList<String[]> getReportAccounts() {
log.debug("looking for accounts");
List<Map<String, Object>> resultList;
String[] accountDetails;
ArrayList<String[]> accounts = new ArrayList<String[]>();
try{
log.debug("Excecuting Query");
resultList = jdbcTemplate.queryForList(SELECT_ALL_ACCOUNT_INFO);
log.debug("Query Results");
log.debug(resultList.toString());
if(resultList != null && resultList.size() > 0){
for(Map<String, Object> temprow: resultList){
log.debug("Mapping Query Results to Account POJO");
accountDetails = new String[3];
accountDetails[0] = (String) temprow.get("acct_name");
accountDetails[1] = (String) temprow.get("login_name");
accountDetails[2] = (String) temprow.get("pswd");
log.debug("Single account details");
log.debug(accountDetails.toString());
log.debug("Adding single account to accounts array");
accounts.add(accountDetails);
}
}
return accounts;
} catch (Exception e){
log.debug("NO RESULTS: " + e);
System.out.println("NO RESULTS: " + e);
return null;
}
}
}
XML
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:annotation-config/>
<context:component-scan base-package="org.test.testpackage.report" annotation-config="false" />
<context:property-placeholder location="classpath:project-be.properties" />
<import resource="classpath:db-config.xml"/>
<bean id="pgReportService" name="pgReportService" class="org.test.testpackage.report.service.AccountLookup" scope="singleton" />
<bean id="jdbcTemplate" name="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource" />
</bean>
Thanks so much!
The question is hard to read. What are you executing that is actually generating a NullPointerException?
For what I understand of your question you might be having a common mistake in Spring: If your variable jdbcTemplate is autowired in the ReportDaoImpl class then this class must be created also by autowiring and not by manual instantiation. Same thing happens for DataSource.
This means:
ReportDaoImpl reportDaoImp = new ReportDaoImpl(dataSource);
will not have an instatiated dataSource (non instantiated jdbcTemplate), because it is you who is actually making the instantiation and not letting Spring to do it.
So you need to specify a bean for ReportDaoImpl in your Application class, such as:
#Autowired
public ReportDaoImpl(DataSource dataSource){
ReportDaoImpl reportDaoImp = new ReportDaoImp();
reportDaoImp.setDataSource(dataSource);
return reportDaoImp;
}
And in the class where you use ReportDaoImp define an attribute:
#Autowired
ReportDaoImpl reportDaoImp;
This will instantiate DataSource (if the a bean for DataSource is also defined) and then instantiate a ReportDaoImpl passing this instance of DataSource.
Edit:
Actually this question answers might answer yours: Why is my Spring #Autowired field null?

Spring Environment Property Source Configuration

I'm working on an application library with a utility class called "Config" which is backed by the Spring Environment object and provides strongly typed getters for all the applications configuration values.
The property sources for the configuration can vary depending on environment (DEV/PROD) and usage (standalone/test/webapp), and can range from the default ones (system & env props) to custom database and JNDI sources.
What I'm struggling with is how to let the apps consuming this library easily configure the property source(s) used by Environment, such that the properties are available for use in our Config class and via the PropertySourcesPlaceholderConfigurer.
We're still using XML configuration, so ideally this could be configured in XML something like.
<bean id="propertySources" class="...">
<property name="sources">
<list>
<ref local="jndiPropertySource"/>
<ref local="databasePropertySource"/>
</list>
</property>
</bean>
...and then injected somehow into the Environment's property sources collection.
I've read that something like this may not be possible due to the timing of the app context lifecycle, and that this may need to be done using an application initializer class.
Any ideas?
It depends on how you want to use the properties, if it is to inject the properties using ${propertyname} syntax, then yes just having PropertySourcesPlaceHolderConfigurer will work, which internally has access to the PropertySources registered in the environment.
If you plan to use Environment directly, using say env.getProperty(), then you are right - the properties using PropertySourcesPlaceHolderConfigurer are not visible here. The only way then is to inject it using Java code, there are two ways that I know of:
a. Using Java Config:
#Configuration
#PropertySource("classpath:/app.properties")
public class SpringConfig{
}
b. Using a custom ApplicationContextInitializer, the way it is described here
I came up with the following which seems to work, but I'm fairly new to Spring, so I'm not so sure how it will hold up under different use cases.
Basically, the approach is to extend PropertySourcesPlaceholderConfigurer and add a setter to allow the user to easily configure a List of PropertySource objects in XML. After creation, the property sources are copied to the current Environment.
This basically allows the property sources to be configured in one place, but used by both placholder configuration and Environment.getProperty scenarios.
Extended PropertySourcesPlaceholderConfigurer
public class ConfigSourcesConfigurer
extends PropertySourcesPlaceholderConfigurer
implements EnvironmentAware, InitializingBean {
private Environment environment;
private List<PropertySource> sourceList;
// Allow setting property sources as a List for easier XML configuration
public void setPropertySources(List<PropertySource> propertySources) {
this.sourceList = propertySources;
MutablePropertySources sources = new MutablePropertySources();
copyListToPropertySources(this.sourceList, sources);
super.setPropertySources(sources);
}
#Override
public void setEnvironment(Environment environment) {
// save off Environment for later use
this.environment = environment;
super.setEnvironment(environment);
}
#Override
public void afterPropertiesSet() throws Exception {
// Copy property sources to Environment
MutablePropertySources envPropSources = ((ConfigurableEnvironment)environment).getPropertySources();
copyListToPropertySources(this.sourceList, envPropSources);
}
private void copyListToPropertySources(List<PropertySource> list, MutablePropertySources sources) {
// iterate in reverse order to insure ordering in property sources object
for(int i = list.size() - 1; i >= 0; i--) {
sources.addFirst(list.get(i));
}
}
}
beans.xml file showing basic configuration
<beans>
<context:annotation-config/>
<context:component-scan base-package="com.mycompany" />
<bean class="com.mycompany.ConfigSourcesConfigurer">
<property name="propertySources">
<list>
<bean class="org.mycompany.CustomPropertySource" />
<bean class="org.springframework.core.io.support.ResourcePropertySource">
<constructor-arg value="classpath:default-config.properties" />
</bean>
</list>
</property>
</bean>
<bean class="com.mycompany.TestBean">
<property name="stringValue" value="${placeholder}" />
</bean>
</beans>
The following worked for me with Spring 3.2.4 .
PropertySourcesPlaceholderConfigurer must be registered statically in order to process the placeholders.
The custom property source is registered in the init method and as the default property sources are already registered, it can itself be parameterized using placeholders.
JavaConfig class:
#Configuration
#PropertySource("classpath:propertiesTest2.properties")
public class TestConfig {
#Autowired
private ConfigurableEnvironment env;
#Value("${param:NOVALUE}")
private String param;
#PostConstruct
public void init() {
env.getPropertySources().addFirst(new CustomPropertySource(param));
}
#Bean
public static PropertySourcesPlaceholderConfigurer placeHolderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
#Bean
public TestBean1 testBean1() {
return new TestBean1();
}
}
Custom property source:
public class CustomPropertySource extends PropertySource<Object> {
public CustomPropertySource(String param) {
super("custom");
System.out.println("Custom property source initialized with param " + param + ".");
}
#Override
public Object getProperty(String name) {
return "IT WORKS";
}
}
Test bean (getValue() will output "IT WORKS"):
public class TestBean1 {
#Value("${value:NOVALUE}")
private String value;
public String getValue() {
return value;
}
}
I had a similar problem, in my case I'm using Spring in a standalone application, after load the default configurations I may need apply another properties file (lazy load configs) present in a config directory. My solution was inspired this Spring Boot documentation, but with no dependency of Spring Boot. See below the source code:
#PropertySources(#PropertySource(value = "classpath:myapp-default.properties"))
public class PersistenceConfiguration {
private final Logger log = LoggerFactory.getLogger(getClass());
private ConfigurableEnvironment env;
#Bean
public static PropertySourcesPlaceholderConfigurer placeholderConfigurerDev(ConfigurableEnvironment env) {
return new PropertySourcesPlaceholderConfigurer();
}
#Autowired
public void setConfigurableEnvironment(ConfigurableEnvironment env) {
for(String profile: env.getActiveProfiles()) {
final String fileName = "myapp-" + profile + ".properties";
final Resource resource = new ClassPathResource(fileName);
if (resource.exists()) {
try {
MutablePropertySources sources = env.getPropertySources();
sources.addFirst(new PropertiesPropertySource(fileName,PropertiesLoaderUtils.loadProperties(resource)));
} catch (Exception ex) {
log.error(ex.getMessage(), ex);
throw new RuntimeException(ex.getMessage(), ex);
}
}
}
this.env = env;
}
...
}
I recently ran into the issue of how to register custom property sources in the environment. My specific problem is that I have a library with a Spring configuration that I want to be imported into the Spring application context, and it requires custom property sources. However, I don't necessarily have control over all of the places where the application context is created. Because of this, I do not want to use the recommended mechanisms of ApplicationContextInitializer or register-before-refresh in order to register the custom property sources.
What I found really frustrating is that using the old PropertyPlaceholderConfigurer, it was easy to subclass and customize the configurers completely within the Spring configuration. In contrast, to customize property sources, we are told that we have to do it not in the Spring configuration itself, but before the application context is initialized.
After some research and trial and error, I discovered that it is possible to register custom property sources from inside of the Spring configuration, but you have to be careful how you do it. The sources need to be registered before any PropertySourcesPlaceholderConfigurers execute in the context. You can do this by making the source registration a BeanFactoryPostProcessor with PriorityOrdered and an order that is higher precedence than the PropertySourcesPlaceholderConfigurer that uses the sources.
I wrote this class, which does the job:
package example;
import java.io.IOException;
import java.util.Properties;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanInitializationException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.Ordered;
import org.springframework.core.PriorityOrdered;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.support.PropertiesLoaderSupport;
/**
* This is an abstract base class that can be extended by any class that wishes
* to become a custom property source in the Spring context.
* <p>
* This extends from the standard Spring class PropertiesLoaderSupport, which
* contains properties that specify property resource locations, plus methods
* for loading properties from specified resources. These are all available to
* be used from the Spring configuration, and by subclasses of this class.
* <p>
* This also implements a number of Spring flag interfaces, all of which are
* required to maneuver instances of this class into a position where they can
* register their property sources BEFORE PropertySourcesPlaceholderConfigurer
* executes to substitute variables in the Spring configuration:
* <ul>
* <li>BeanFactoryPostProcessor - Guarantees that this bean will be instantiated
* before other beans in the context. It also puts it in the same phase as
* PropertySourcesPlaceholderConfigurer, which is also a BFPP. The
* postProcessBeanFactory method is used to register the property source.</li>
* <li>PriorityOrdered - Allows the bean priority to be specified relative to
* PropertySourcesPlaceholderConfigurer so that this bean can be executed first.
* </li>
* <li>ApplicationContextAware - Provides access to the application context and
* its environment so that the created property source can be registered.</li>
* </ul>
* <p>
* The Spring configuration for subclasses should contain the following
* properties:
* <ul>
* <li>propertySourceName - The name of the property source this will register.</li>
* <li>location(s) - The location from which properties will be loaded.</li>
* <li>addBeforeSourceName (optional) - If specified, the resulting property
* source will be added before the given property source name, and will
* therefore take precedence.</li>
* <li>order (optional) - The order in which this source should be executed
* relative to other BeanFactoryPostProcessors. This should be used in
* conjunction with addBeforeName so that if property source factory "psfa"
* needs to register its property source before the one from "psfb", "psfa"
* executes AFTER "psfb".
* </ul>
*
* #author rjsmith2
*
*/
public abstract class AbstractPropertySourceFactory extends
PropertiesLoaderSupport implements ApplicationContextAware,
PriorityOrdered, BeanFactoryPostProcessor {
// Default order will be barely higher than the default for
// PropertySourcesPlaceholderConfigurer.
private int order = Ordered.LOWEST_PRECEDENCE - 1;
private String propertySourceName;
private String addBeforeSourceName;
private ApplicationContext applicationContext;
private MutablePropertySources getPropertySources() {
final Environment env = applicationContext.getEnvironment();
if (!(env instanceof ConfigurableEnvironment)) {
throw new IllegalStateException(
"Cannot get environment for Spring application context");
}
return ((ConfigurableEnvironment) env).getPropertySources();
}
public int getOrder() {
return order;
}
public void setOrder(int order) {
this.order = order;
}
public String getPropertySourceName() {
return propertySourceName;
}
public void setPropertySourceName(String propertySourceName) {
this.propertySourceName = propertySourceName;
}
public String getAddBeforeSourceName() {
return addBeforeSourceName;
}
public void setAddBeforeSourceName(String addBeforeSourceName) {
this.addBeforeSourceName = addBeforeSourceName;
}
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
/**
* Subclasses can override this method to perform adjustments on the
* properties after they are read.
* <p>
* This should be done by getting, adding, removing, and updating properties
* as needed.
*
* #param props
* properties to adjust
*/
protected void convertProperties(Properties props) {
// Override in subclass to perform conversions.
}
/**
* Creates a property source from the specified locations.
*
* #return PropertiesPropertySource instance containing the read properties
* #throws IOException
* if properties cannot be read
*/
protected PropertySource<?> createPropertySource() throws IOException {
if (propertySourceName == null) {
throw new IllegalStateException("No property source name specified");
}
// Load the properties file (or files) from specified locations.
final Properties props = new Properties();
loadProperties(props);
// Convert properties as required.
convertProperties(props);
// Convert to property source.
final PropertiesPropertySource source = new PropertiesPropertySource(
propertySourceName, props);
return source;
}
#Override
public void postProcessBeanFactory(
ConfigurableListableBeanFactory beanFactory) throws BeansException {
try {
// Create the property source, and get its desired position in
// the list of sources.
if (logger.isDebugEnabled()) {
logger.debug("Creating property source [" + propertySourceName
+ "]");
}
final PropertySource<?> source = createPropertySource();
// Register the property source.
final MutablePropertySources sources = getPropertySources();
if (addBeforeSourceName != null) {
if (sources.contains(addBeforeSourceName)) {
if (logger.isDebugEnabled()) {
logger.debug("Adding property source ["
+ propertySourceName + "] before ["
+ addBeforeSourceName + "]");
}
sources.addBefore(addBeforeSourceName, source);
} else {
logger.warn("Property source [" + propertySourceName
+ "] cannot be added before non-existent source ["
+ addBeforeSourceName + "] - adding at the end");
sources.addLast(source);
}
} else {
if (logger.isDebugEnabled()) {
logger.debug("Adding property source ["
+ propertySourceName + "] at the end");
}
sources.addLast(source);
}
} catch (Exception e) {
throw new BeanInitializationException(
"Failed to register property source", e);
}
}
}
Of note here is that the default order of this property source factory class is higher precedence than the default order of PropertySourcesPlaceholderConfigurer.
Also, the registration of the property source happens in postProcessBeanFactory, which means that it will execute in the correct order relative to the PropertySourcesPlaceholderConfigurer. I discovered the hard way that InitializingBean and afterPropertiesSet do not respect the order parameter, and I gave up on that approach as being wrong and redundant.
Finally, because this is a BeanFactoryPostProcessor, it is a bad idea to try to wire much in the way of dependencies. Therefore, the class accesses the environment directly through the application context, which it obtains using ApplicationContextAware.
In my case, I needed the property source to decrypt password properties, which I implemented using the following subclass:
package example;
import java.util.Properties;
/**
* This is a property source factory that creates a property source that can
* process properties for substituting into a Spring configuration.
* <p>
* The only thing that distinguishes this from a normal Spring property source
* is that it decrypts encrypted passwords.
*
* #author rjsmith2
*
*/
public class PasswordPropertySourceFactory extends
AbstractPropertySourceFactory {
private static final PasswordHelper passwordHelper = new PasswordHelper();
private String[] passwordProperties;
public String[] getPasswordProperties() {
return passwordProperties;
}
public void setPasswordProperties(String[] passwordProperties) {
this.passwordProperties = passwordProperties;
}
public void setPasswordProperty(String passwordProperty) {
this.passwordProperties = new String[] { passwordProperty };
}
#Override
protected void convertProperties(Properties props) {
// Adjust password fields by decrypting them.
if (passwordProperties != null) {
for (String propName : passwordProperties) {
final String propValue = props.getProperty(propName);
if (propValue != null) {
final String plaintext = passwordHelper
.decryptString(propValue);
props.setProperty(propName, plaintext);
}
}
}
}
}
Finally, I specifed the property source factory in my Spring configuration:
<!-- Enable property resolution via PropertySourcesPlaceholderConfigurer.
The order has to be larger than the ones used by custom property sources
so that those property sources are registered before any placeholders
are substituted. -->
<context:property-placeholder order="1000" ignore-unresolvable="true" />
<!-- Register a custom property source that reads DB properties, and
decrypts the database password. -->
<bean class="example.PasswordPropertySourceFactory">
<property name="propertySourceName" value="DBPropertySource" />
<property name="location" value="classpath:db.properties" />
<property name="passwordProperty" value="db.password" />
<property name="ignoreResourceNotFound" value="true" />
<!-- Order must be lower than on property-placeholder element. -->
<property name="order" value="100" />
</bean>
To be honest, with the defaults for order in PropertySourcesPlaceholderConfigurer and AbstractPropertySourceFactory, it is probably not even necessary to specify order in the Spring configuration.
Nonetheless, this works, and it does not require any fiddling with the application context initialization.

Resources