Posts Tagged ‘injection’

Spring and string list injection

Saturday, January 12th, 2013

This week, I have been bitten by a surprising behavior of Spring type conversion. Let's have the following bean and Spring config.

public class Bean {
    public void setArray(String[] values) {
        System.out.println("array: " + Arrays.toString(values));
        System.out.println(values.length);
    }

    public void setList(List<String> values) {
        System.out.println("list: " + values);
        System.out.println(values.size());
    }
}
<bean class="net.javacrumbs.Bean">
    <property name="array" value="item1, item2"/>
    <property name="list" value="item1, item2"/>
</bean>

We are trying to inject a String containing comma delimited list of values. The question is whether the String will be automatically converted to list of multiple items in array/list or only one item string will be injected. Both options make sense. What do you think? What will be the length of the array/list? One or two?

The answer is surprising. The array value will be split to two Strings whereas the List will have only one element. The output will be:

array: [item1, item2]
2
list: [item1, item2]
1

It's an easy error to make but hard one to spot. If you log the values, the output is undistinguishable. If you look at the output above, the values are exactly the same. What's worse you usually do not have unit tests to test Spring configuration. In my case the bug got as far as production environment. On test environments we have got only one value in the property so the bug went unnoticed. On the production environment we have set a comma separated list of values. And of course, I have expected the array behavior while using list parameter. Silly me.

Spring managed Hibernate interceptor in JPA

Saturday, January 24th, 2009

I have been trying to teach Hibernate injecting dependencies into Entities (I know, there is magic @Configurable annotation, I wanted to try it without magic). It is quite easy to do it using Hibernate interceptor (for example like this). But there is one drawback. It is not straightforward to inject interceptor into Hibernate when JPA abstraction is in the way.

It is simple to define interceptor in persistence.xml using hibernate.ejb.interceptor property. But it is only possible to specify class name, you can not inject Spring bean. If you read documentation to Spring LocalContainerEntityManagerFactoryBean there is no possibility to specify the interceptor bean there neither. But there is a way how to achieve it. First of all, you have to redefine Hibernate PersistenceProvider.

public class ConfigurableHibernatePersistence extends HibernatePersistence {
	private Interceptor interceptor;
	public Interceptor getInterceptor() {
		return interceptor;
	}

	public void setInterceptor(Interceptor interceptor) {
		this.interceptor = interceptor;
	}

	@SuppressWarnings("unchecked")
	@Override
	public EntityManagerFactory createContainerEntityManagerFactory(PersistenceUnitInfo info, Map map) {
		Ejb3Configuration cfg = new Ejb3Configuration();
		Ejb3Configuration configured = cfg.configure( info, map );
		postprocessConfiguration(info, map, configured);
		return configured != null ? configured.buildEntityManagerFactory() : null;
	}

	@SuppressWarnings("unchecked")
	protected void postprocessConfiguration(PersistenceUnitInfo info, Map map, Ejb3Configuration configured) {
		if (this.interceptor != null)
		{
			if (configured.getInterceptor()==null || EmptyInterceptor.class.equals(configured.getInterceptor().getClass()))
			{
				configured.setInterceptor(this.interceptor);
			}
			else
			{
				throw new IllegalStateException("Hibernate interceptor already set in persistence.xml ("+configured.getInterceptor()+")");
			}
		}
	}
}

(source)

Here we override method createContainerEntityManagerFactory in order to add postprocessConfiguration method call. In this method, it is possible to change Hibernate configuration as needed. Now the only thing to be done is configuring Spring to use our new PersistenceProvider. It is quite simple.

	<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
		<property name="persistenceUnitName" value="testPU" />
		<property name="dataSource" ref="dataSource" />
		<property name="persistenceProvider">
			<bean class="net.krecan.javacrumbs.jpa.interceptor.ConfigurableHibernatePersistence">
				<property name="interceptor">
					<bean class="net.krecan.javacrumbs.jpa.interceptor.SpringInjectingInterceptor"/>
				</property>
			</bean>
		</property>
	</bean>

And that all folks, we have Spring configured interceptor even when using JPA abstraction. It would be better if similar classes were in Spring or Hibernate, but it's just few lines of code so feel free to copy them if you need to. If you know easier way how to do it, please mention it in the comments below.