Setter injection in Spring - 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

Related

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

Spring TypeConverter fails on Camel RouteBuilder subclass

In an attempt to install my component on karaf I get the following error:
Caused by: org.apache.camel.CamelException: Cannot find any routes with this RouteBuilder reference: RouteBuilderRef[logparserRouteBean]
I've narrowed it down to a conversion error in AbstractBeanFactory using the SimpleTypeConverter as returned by the getTypeConverter().
Given that PerformanceLogRoute extends org.apache.camel.builder.RouteBuilder, how can the convertion fail??
Suggestions, and ideas to any solution is greatly appreciated.
UPDATE
package no.osl.cdms.profile.routes;
import no.osl.cdms.profile.api.TimeMeasurement;
import no.osl.cdms.profile.factories.EntityFactory;
import no.osl.cdms.profile.log.TimeMeasurementEntity;
import no.osl.cdms.profile.parser.LogLineRegexParser;
import org.apache.camel.builder.RouteBuilder;
import java.util.Map;
public class PerformanceLogRoute extends RouteBuilder {
public static final String PERFORMANCE_LOG_ROUTE_ID = "PerformanceLogRoute";
private static final String LOG_DIRECTORY = "C:/data";
private static final String LOG_FILE = "performance.log";
private static final int DELAY = 0;
private LogLineRegexParser logLineRegexParser = new LogLineRegexParser();
private EntityFactory entityFactory = EntityFactory.getInstance();
private static final String LOG_FILE_ENDPOINT = "stream:file? fileName="+LOG_DIRECTORY +"/"+LOG_FILE+"&scanStream=true&scanStreamDelay=" + DELAY;
private static final String DATABASE_ENDPOINT = "jpa:";
#Override
public void configure() throws Exception{
fromF(LOG_FILE_ENDPOINT, LOG_DIRECTORY, LOG_FILE, DELAY)
.convertBodyTo(String.class) // Converts input to String
.choice().when(body().isGreaterThan("")) // Ignores empty lines
.bean(logLineRegexParser, "parse") // Parses log entry into String map
.bean(entityFactory, "createTimemeasurement") // Parses log entry into database format
.split(body())
.choice().when(body().isNotNull())
.toF(DATABASE_ENDPOINT, body().getClass().toString())
.routeId(PERFORMANCE_LOG_ROUTE_ID);
}
public String toString() {
return PERFORMANCE_LOG_ROUTE_ID;
}
}
The 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:camel="http://camel.apache.org/schema/spring"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd">
<bean id="logparserRouteBean" class="no.osl.cdms.profile.routes.PerformanceLogRoute" />
<camelContext id="cdms-core-camel-context" xmlns="http://camel.apache.org/schema/spring">
<routeBuilder ref="logparserRouteBean" />
</camelContext>
</beans>
This is what I found at this moment. From what I remember it is identical to what caused the error, but I'll double check in the morning.

JAXB marshal/unmarshal - ing SWT.Image or AWT BufferedImage

I am trying to marshal with jaxb an object which contains an Image, and afterwards unmarshalling it (i.e. save/load).
Is there a way to store that image?
I am trying to create a function which returns the byte array describing the swt.image imagedata, but once I marked it as an #XmlElement, the process of storing it fails throwing an Exception like this:
com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException: 1 counts of IllegalAnnotationExceptions
JAXB annotation is placed on a method that is not a JAXB property
this problem is related to the following location:
at #javax.xml.bind.annotation.XmlElement()
Also, I have tested to convert the SWT.Image to a AWT.BufferedImage, but I still get the same Exception.
Your exception indicates that you have placed an annotation on a method that isn't an accessor (get/set method). Below is an example using a java.awt.Image property:
Root
package forum9094655;
import java.awt.Image;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class Root {
private Image image;
public Image getImage() {
return image;
}
public void setImage(Image image) {
this.image = image;
}
}
Demo
package forum9094655;
import java.awt.Image;
import java.awt.image.BufferedImage;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Root.class);
Root root = new Root();
Image image = new BufferedImage(1,1,BufferedImage.TYPE_INT_RGB);
root.setImage(image);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(root, System.out);
}
}
Output
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
<image>iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR42mNgYGAAAAAEAAHI6uv5AAAAAElFTkSuQmCC</image>
</root>

Maven plugin to validate Spring configuration?

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

Using #Table with schema name in Hibernate 3.3.1ga and HSQLDB

How can I make this work in unit tests using Hibernate 3.3.1ga and HSQLDB:
#Entity
#Table(name="CATEGORY", schema="TEST")
public static class Category { ... }
The problem is that Hibernate expects the schema to exist. The second problem is that Hibernate issues the CREATE TABLE TEST.CATEGORY before any of my code runs (this happens deep inside Spring's test setup), so I can't get a connection to the DB before Hibernate and create the schema manually.
But I need the schema because I have to access different databases in the real code. What should I do?
Hibernate 3.3.1ga, HSQLDB, Spring 2.5
You could write a class that implements InitializingBean:
public class SchemaCreator implements InitializingBean {
private String schema;
private DataSource dataSource;
public String getSchema() {
return schema;
}
public void setSchema(String schema) {
this.schema = schema;
}
public DataSource getDataSource() {
return dataSource;
}
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
#Override
public void afterPropertiesSet() throws Exception {
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
jdbcTemplate.execute("CREATE SCHEMA " + schema + " AUTHORIZATION DBA");
}
}
You then have to define a bean in your bean definition file of this class (I'm taking a shot in the dark as to what your existing bean definitions look like).
<bean id="dataSource" class="...">
<property name="driverClassName" value="org.hsqldb.jdbcDriver"/>
<property name="url" value="jdbc:hsqldb:mem:test"/>
<property name="username" value="sa"/>
<property name="password" value=""/>
</bean>
<bean id="sessionFactory" depends-on="schemaCreator" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
...
</bean>
<bean id="schemaCreator" class="SchemaCreator">
<property name="dataSource" ref="dataSource"/>
<property name="schema" value="TEST"/>
</bean>
By using the depends-on attribute of Hibernate's bean, Spring will ensure that the schemaCreator bean will be initialized first, causing the schema to exist just in time. This should also make your intentions clearer.
My current solution looks like this:
#Override
protected String[] getConfigLocations() {
createHSQLDBSchemas ();
return new String[]{
"test-spring-config.xml"
};
}
private static boolean hsqldbSchemasCreated = false;
public static void createHSQLDBSchemas ()
{
if (hsqldbSchemasCreated)
return;
try
{
log.info ("createHSQLDBSchemas");
Class.forName("org.hsqldb.jdbcDriver").newInstance();
Connection c = DriverManager.getConnection("jdbc:hsqldb:mem:test", "sa", "");
Statement stmt = c.createStatement ();
String sql;
sql = "CREATE SCHEMA xxx AUTHORIZATION DBA";
log.info (sql);
stmt.execute (sql);
stmt.close ();
c.close ();
}
catch (Exception e)
{
throw new ShouldNotHappenException (e);
}
hsqldbSchemasCreated = true;
}
but that feels like a really ugly hack. Isn't there a better solution?
Below is an example of how you can create spring config with test hslqdb
It automaticly detects all your schemas from #Table(schema =...) and creates them for you.
If it is just for testing this should work for you:
import org.reflections.Reflections; //maven artifact: 'org.reflections:reflections:0.9.9-RC1'
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.io.ClassPathResource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.hibernate4.LocalSessionFactoryBean;
import javax.persistence.Table;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;
#Configuration
#ComponentScan("com.test.collection")
public class CollectionConfig {
private static final String[] ENTITY_PACKAGES = { "com.test.collection.domain.dao" };
private static final String CONFIGURATION_LOCATION = "/movie-collection-hibernate.cfg.xml";
#Bean( name = "testSessionFactory" )
#Lazy
public LocalSessionFactoryBean getTestSessionFactory() {
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setPackagesToScan( ENTITY_PACKAGES );
Properties hibernateProperties = getHibernateHsqlTestDbProperties();
sessionFactory.setHibernateProperties( hibernateProperties );
createNonStandardSchemas( hibernateProperties );
return sessionFactory;
}
private void createNonStandardSchemas( Properties properties ) {
final String DEFAULT_SCHEMA = "";
Set<String> schemas = new HashSet<>();
Reflections reflections = new Reflections( ENTITY_PACKAGES );
Set<Class<?>> annotatedClasses =
reflections.getTypesAnnotatedWith( Table.class );
for ( Class<?> clazz : annotatedClasses ) {
Table table = clazz.getAnnotation( Table.class );
if ( !DEFAULT_SCHEMA.equals( table.schema() ) ) {
schemas.add( table.schema() );
}
}
if ( !schemas.isEmpty() ) {
DriverManagerDataSource driverManager = new DriverManagerDataSource();
driverManager.setDriverClassName( properties.getProperty( "hibernate.connection.driver_class" ) );
driverManager.setUrl( properties.getProperty( "hibernate.connection.url" ) );
driverManager.setUsername( properties.getProperty( "hibernate.connection.username" ) );
driverManager.setPassword( properties.getProperty( "hibernate.connection.password" ) );
JdbcTemplate jdbcTemplate = new JdbcTemplate( driverManager );
for ( String schemaName : schemas ) {
jdbcTemplate.execute(
String.format( "DROP SCHEMA IF EXISTS %s", schemaName)
);
jdbcTemplate.execute(
String.format( "CREATE SCHEMA %s AUTHORIZATION DBA", schemaName)
);
}
}
}
private Properties getHibernateHsqlTestDbProperties() {
Properties prop = new Properties();
prop.setProperty( "hibernate.connection.driver_class", "org.hsqldb.jdbcDriver" );
prop.setProperty( "hibernate.connection.url", "jdbc:hsqldb:mem:test" );
prop.setProperty( "hibernate.connection.username", "sa" );
prop.setProperty( "hibernate.connection.password", "test" );
prop.setProperty( "hibernate.connection.pool_size", "5" );
prop.setProperty( "hibernate.dialect", "org.hibernate.dialect.HSQLDialect" );
prop.setProperty( "hibernate.current_session_context_class", "thread" );
prop.setProperty( "hibernate.cache.provider_class", "org.hibernate.cache.internal.NoCachingRegionFactory" );
prop.setProperty( "hibernate.show_sql", "false" );
prop.setProperty( "hibernate.format_sql", "false" );
prop.setProperty( "hibernate.use_sql_comments", "false" );
prop.setProperty( "hibernate.hbm2ddl.auto", "create-drop" );
return prop;
}
}
And here is a test sample:
#ContextConfiguration( classes = CollectionConfig.class )
#DirtiesContext( classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD )
public class DaoMappingTest extends AbstractTestNGSpringContextTests {
#Autowired
private SessionFactory testSessionFactory;
#Test
public void thatMovieIsSaved() {
Movie killBill = getKillBillMovie0();
saveToDb( Arrays.asList(killBill) );
Session querySession = testSessionFactory.openSession();
List<Movie> movies = querySession.createQuery( "from Movie" ).list();
querySession.close();
assertThat( movies ).containsExactly( killBill );
}
#Test
public void that2MoviesIsSaved() {
Movie killBill = getKillBillMovie0();
Movie terminator = getTerminatorMovie1();
saveToDb( Arrays.asList( killBill, terminator ) );
Session querySession = testSessionFactory.openSession();
List<Movie> movies = querySession.createQuery( "from Movie" ).list();
querySession.close();
assertThat( movies ).containsOnly( killBill, terminator );
}
private void saveToDb( List<?> objects ) {
Session session = testSessionFactory.openSession();
session.beginTransaction();
for( Object obj : objects) {
session.save( obj );
}
session.getTransaction().commit();
session.close();
}
#AfterSuite
public void tearDown() {
testSessionFactory.close();
}
}
It looks to me like you have a reproducible bug in the Hibernate DDL creation code. You should report a bug - it's a long term solution but it's the way things are done in open source. Of course you might want to produce a patch, but I never found the Hibernate code base easy to hack.
I ran into the same problem where MS SQL Server wants the catalog and schema defined, but HSQLDB does not. My solution was to load a custom orm.xml file (via persistence.xml) specifically for MS SQL Server that sets the catalog and schema.
1.Only specify the #Table name (omit any catalog or schema info) for your entity:
#Entity
#Table(name="CATEGORY")
public static class Category { ... }
2.Specify two persistence-unit nodes in your META-INF/persistence.xml file
<persistence version="2.0"
xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<!--
| For production and integration testing we use MS SQL Server, which needs
| the catalog and schema set (see orm-mssql.xml).
|-->
<persistence-unit name="com.mycompany.prod">
<mapping-file>META-INF/orm-mssql.xml</mapping-file>
</persistence-unit>
<!--
| For unit testing we use HSQLDB, which does not need the catalog or schema.
|-->
<persistence-unit name="com.mycompany.test" />
</persistence>
3.Specify the default catalog and schema in the orm-mssql.xml file:
<entity-mappings version="2.0"
xmlns="http://java.sun.com/xml/ns/persistence/orm"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm orm_2_0.xsd">
<persistence-unit-metadata>
<!--
| Set the catalog and schema for MS SQL Server
|-->
<persistence-unit-defaults>
<schema>MYSCHEMA</schema>
<catalog>MYCATALOG</catalog>
</persistence-unit-defaults>
</persistence-unit-metadata>
</entity-mappings>
4.I'm using Spring to configure JPA, so I use a property-placeholder for the value of the persistenceUnitName:
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="jpaVendorAdapter" ref="jpaVendorAdapter" />
<property name="persistenceUnitName" value="${entityManagerFactory.persistenceUnitName}" />
</bean>
For unit tests, use 'com.mycompany.test' and for integration-tests/production deployments, use 'com.mycompany.prod'.

Resources