Using alternative credentials for Liquibase in Spring Boot

One of the projects I’m working uses Spring Boot to handle all database changes for each micro-service. One of the obvious requirements to make this work is a database user with DBA rights, otherwise it can not create, alter or drop tables.

You could configure the default datasource to use such user, but this would mean that every component will use this datasource and in case of a security breach (eg. SQL injection) this would be bad because all of a sudden someone else has DBA access to your database.

Therefore it’s best to configure a second datasource for Liquibase with a DBA user and a primary datasource with a read-write database user.

Configuring the Liquibase datasource

	@LiquibaseDataSource
	@Bean
	public DataSource liquibaseDataSource() {
		DataSource ds =  DataSourceBuilder.create()
				.username(liquibaseDataSourceProperties.getUser())
				.password(liquibaseDataSourceProperties.getPassword())
				.url(liquibaseDataSourceProperties.getUrl())
				.driverClassName(liquibaseDataSourceProperties.getDriver())
				.build();
		if (ds instanceof org.apache.tomcat.jdbc.pool.DataSource) {
			((org.apache.tomcat.jdbc.pool.DataSource) ds).setInitialSize(0);
			((org.apache.tomcat.jdbc.pool.DataSource) ds).setMaxActive(2);
			((org.apache.tomcat.jdbc.pool.DataSource) ds).setMaxAge(30000);
			((org.apache.tomcat.jdbc.pool.DataSource) ds).setMinIdle(0);
			((org.apache.tomcat.jdbc.pool.DataSource) ds).setMinEvictableIdleTimeMillis(60000);
		} else {
			LOG.warn("#################################################################");
			LOG.warn("Datasource was not of type org.apache.tomcat.jdbc.pool.DataSource");
			LOG.warn("but was of type {}", ds.getClass().getName());
			LOG.warn("Number of leaked connections might be 10 per instance !");
			LOG.warn("#################################################################");
		}

		LOG.info("Initialized a datasource for {}", liquibaseDataSourceProperties.getUrl());
		return ds;
	}

FYI: LiquibaseDataSourceProperties is just a standard bean annotated with

@Component
@ConfigurationProperties("datasource.liquibase")

in order to have different configurations per environment. Just must configure the pool to only use one connection and to release this connection after a while, otherwise your user will keep 10 connections open. With 10 micro-services which can be up- and down-scaled you’ll quickly block over 100 database connections which might prevent your application to make new connections. In our case Spring uses the default Tomcat pool as it’s readily available on the classpath, but it might be different for your setup.
For more info see the original Stackoverflow question.

Configuring the default datasource

If you already have a datasource configured in your application then you just need to annotate it with  the @Primary annotation to make sure that this read-write user is the one used by all the other Spring components. If you don’t do this then Spring Boot won’t start because you have 2 instances of DataSource configured and Spring doesn’t know which one to pick.

 

 

No inserts using Hibernate and Spring

I was setting up a simple side-project with Spring, Spring Data and Hibernate. My repositories are generated using Spring Data’s jpa:repositories to save on development time and the database schema is generated using Hibernate, based on my JPA and JSR-303 annotated entities.

However, one of the issues I encountered was that my repositories did not insert anything in the database. After searching the web I followed a suggestion on Stackoverflow to add an entityManager.flush() to the repository to see if a transaction was running or not. There was indeed no transaction running but I had yet to find out why.

Exception in thread "main" javax.persistence.TransactionRequiredException: no transaction is in progress
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.checkTransactionNeeded(AbstractEntityManagerImpl.java:1136)
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.flush(AbstractEntityManagerImpl.java:1297)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at org.springframework.orm.jpa.ExtendedEntityManagerCreator$ExtendedEntityManagerInvocationHandler.invoke(ExtendedEntityManagerCreator.java:344)
at com.sun.proxy.$Proxy31.flush(Unknown Source)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:293)
at com.sun.proxy.$Proxy31.flush(Unknown Source)
at be.pw999.photocol.service.PhotoColServiceImpl.makeImage(PhotoColServiceImpl.java:45)
...

At that point my code looked like this:

@Service
public class PhotoColServiceImpl implements PhotoColService {

// My Injected beans

@Autowired
private EntityManager em;

@Transactional(readOnly = false)
@Override
public MakeImageResponse makeImage(MakeImageRequest request) {
em.flush();
}
}

 

 

public class Backend {

 public static void main(String[] args) {
  ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("classpath:/META-INF/webapp/WEB-INF/application-context.xml");
  
  PhotoColService service = ac.getBean(PhotoColService.class);
  
  MakeImageRequest r = new MakeImageRequest("DSC0012.JPG", "/home/phillip/Pictures", "12345678901234567890123456789012");
  MakeImageResponse resp = service.makeImage(r);
  
  ac.close();
 }
}

 

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
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-4.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd
http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.8.xsd">

 <context:component-scan base-package="be.pw999.photocol.service"></context:component-scan>
 <!-- Define your application beans here. They will be available to the
beans defined in your web-context because it is a sub-context. Beans defined
in the web-context will not be available in the application context. -->
 <context:property-placeholder location="classpath:persistence-h2.properties" />

 <bean id="sessionFactory"
class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
  <property name="dataSource" ref="dataSource" />
  <property name="packagesToScan" value="be.pw999.photocol.model" />
  <property name="hibernateProperties">
   <props>
    <prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop>
    <prop key="hibernate.dialect">${hibernate.dialect}</prop>
   </props>
  </property>
 </bean>

 <bean id="dataSource" class="org.apache.tomcat.dbcp.dbcp.BasicDataSource">
  <property name="driverClassName" value="${jdbc.driverClassName}" />
  <property name="url" value="${jdbc.url}" />
  <property name="username" value="${jdbc.user}" />
  <property name="password" value="${jdbc.pass}" />
 </bean>

 <bean id="transactionManager"
class="org.springframework.orm.hibernate5.HibernateTransactionManager">
  <property name="sessionFactory" ref="sessionFactory" />
 </bean>

 <bean id="persistenceExceptionTranslationPostProcessor"
class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" />

 <bean id="jpaVendorAdapter"
class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
  <property name="showSql" value="true" />
  <property name="generateDdl" value="true" />
  <property name="database" value="H2" />
 </bean>

 <bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
  <property name="dataSource" ref="dataSource" />
  <property name="jpaVendorAdapter" ref="jpaVendorAdapter" />
  <!-- spring based scanning for entity classes> -->
  <property name="packagesToScan" value="be.pw999.photocol.model" />
</bean>


 <jpa:repositories base-package="be.pw999.photocol.repository"
transaction-manager-ref="transactionManager"
entity-manager-factory-ref="entityManagerFactory"></jpa:repositories>
</beans>

There were actually a couple of issues with this configuration.

Mistake 1

Using the wrong @Transaction annotation. There are two @Transaction annotations: @javax.persistence.Transaction and @org.springframework.transaction.annotation.Transactional. Only the latter will add transaction management to your Spring services and I had used the first one (because I trust autocomplete too much).

Mistake 2

Second mistake was that I had forgotten to add <tx:annotation-driven /> to my configuration. Stupid, stupid, stupid !

Mistake 3

After solving the first two mistakes I still had no transaction and no inserts in my H2 database. I quickly realized that my transaction manager was an instance of org.springframework.orm.hibernate5.HibernateTransactionManager and even though I use Hibernate, I’m not using it directly. My repositories are generated with Spring’s jpa:repositories and they use a JPA EntityManager instead of the Hibernate SessionFactory. So even though my configuration looked great, the JPA repositories did not use the Hibernate managed transactions.

So instead of using the HibernateTransactionManager, I used the org.springframework.orm.jpa.JpaTransactionManager and this did solve the no-inserts problem.

So this is now my configuration that works:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xmlns:tx="http://www.springframework.org/schema/tx"
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-4.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd
http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.8.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.2.xsd">

 <context:component-scan base-package="be.pw999.photocol.service"></context:component-scan>
 <!-- Define your application beans here. They will be available to the
beans defined in your web-context because it is a sub-context. Beans defined
in the web-context will not be available in the application context. -->
 <context:property-placeholder location="classpath:persistence-h2.properties" />
 <tx:annotation-driven transaction-manager="jpaTransactionManager" />

 <bean id="sessionFactory"
class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
  <property name="dataSource" ref="dataSource" />
  <property name="packagesToScan" value="be.pw999.photocol.model" />
  <property name="hibernateProperties">
   <props>
    <prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop>
    <prop key="hibernate.dialect">${hibernate.dialect}</prop>
   </props>
  </property>
 </bean>

 <bean id="dataSource" class="org.apache.tomcat.dbcp.dbcp.BasicDataSource">
  <property name="driverClassName" value="${jdbc.driverClassName}" />
  <property name="url" value="${jdbc.url}" />
  <property name="username" value="${jdbc.user}" />
  <property name="password" value="${jdbc.pass}" />
 </bean>

 <bean id="transactionManager"
class="org.springframework.orm.hibernate5.HibernateTransactionManager">
  <property name="sessionFactory" ref="sessionFactory" />
 </bean>

 <bean id="persistenceExceptionTranslationPostProcessor"
class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" />

 <bean id="jpaVendorAdapter"
class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
  <property name="showSql" value="true" />
  <property name="generateDdl" value="true" />
  <property name="database" value="H2" />
 </bean>

 <bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
  <property name="dataSource" ref="dataSource" />
  <property name="jpaVendorAdapter" ref="jpaVendorAdapter" />
  <!-- spring based scanning for entity classes> -->
  <property name="packagesToScan" value="be.pw999.photocol.model" />
 </bean>

 <bean id="jpaTransactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
  <property name="entityManagerFactory" ref="entityManagerFactory"></property>
 </bean>

 <jpa:repositories base-package="be.pw999.photocol.repository"
transaction-manager-ref="jpaTransactionManager"
entity-manager-factory-ref="entityManagerFactory"></jpa:repositories>
</beans>

Initialization of bean failed; nested exception is java.lang.IllegalArgumentException: error at ::0 formal unbound in pointcut

I’m currently studying for my Spring 3 certification and I came along this annoying AOP error:

Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'bank' defined in file [C:\core-spring-3.2.1.RELEASE\workspace\Spring-cert\target\classes\be\mycompany\pw999\aop\Bank.class]: Initialization of bean failed; nested exception is java.lang.IllegalArgumentException: error at ::0 formal unbound in pointcut 
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:527)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:456)
	at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:291)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:288)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:190)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:580)
	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:895)
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:425)
	at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:139)
	at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:83)
	at be.mycompany.pw999.aop.Main.main(Main.java:12)
Caused by: java.lang.IllegalArgumentException: error at ::0 formal unbound in pointcut 
	at org.aspectj.weaver.tools.PointcutParser.parsePointcutExpression(PointcutParser.java:302)
	at org.springframework.aop.aspectj.AspectJExpressionPointcut.buildPointcutExpression(AspectJExpressionPointcut.java:195)
	at org.springframework.aop.aspectj.AspectJExpressionPointcut.checkReadyToMatch(AspectJExpressionPointcut.java:181)
	at org.springframework.aop.aspectj.AspectJExpressionPointcut.getClassFilter(AspectJExpressionPointcut.java:162)
	at org.springframework.aop.support.AopUtils.canApply(AopUtils.java:200)
	at org.springframework.aop.support.AopUtils.canApply(AopUtils.java:254)
	at org.springframework.aop.support.AopUtils.findAdvisorsThatCanApply(AopUtils.java:286)
	at org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator.findAdvisorsThatCanApply(AbstractAdvisorAutoProxyCreator.java:117)
	at org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator.findEligibleAdvisors(AbstractAdvisorAutoProxyCreator.java:87)
	at org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator.getAdvicesAndAdvisorsForBean(AbstractAdvisorAutoProxyCreator.java:68)
	at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.wrapIfNecessary(AbstractAutoProxyCreator.java:359)
	at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.postProcessAfterInitialization(AbstractAutoProxyCreator.java:322)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsAfterInitialization(AbstractAutowireCapableBeanFactory.java:407)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1426)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:519)
	... 11 more

My JoinPoint is:

	public Gold buyGold(double amount) {
		return new Gold(amount / 1300.0);
	}

and my Aspect looks like this:

	@AfterReturning(value="execution(* be.mycompany.pw999..Bank+.buyGold(..))", returning="gld")
	public void logGoldOutput(Joinpoint jp, Gold gld) {
		System.out.println("Gold returned: " + gld.amount);
	}

Believe it or not, the error lies in a single character: it’s org.aspectj.lang.JoinPoint and not org.aopalliance.intercept.Joinpoint .

Mass convert a project to UTF-8 using Notepad++

Lately, I had to convert the encoding of a multimodule maven project from our default Cp-1252 encoding to UTF-8. Changing the project settings is rather easy and there are multiple guides availble on the internet, so I won’t re-invent the hot water.

The most dificult task however was converting all our source files from Cp-1252 to UTF-8 and preferably on Windows :) . I’ve been looking into applications that would auto-convert everything for me, but none of them actually converted to content, resulting in garbage files. I almost started converting all the files by hand using Notepad++ when I discovered this process could be automated !

First of all you’ll need to install the Python Script plugin using the Notepad++ Plugin Manager. Then, after installing and restarting Notepad++, you have to create a new script with the following code:

import os;
import sys;
filePathSrc="C:\\Temp\\UTF8"
for root, dirs, files in os.walk(filePathSrc):
	for fn in files:
	  if fn[-4:] != '.jar' and fn[-5:] != '.ear' and fn[-4:] != '.gif' and fn[-4:] != '.jpg' and fn[-5:] != '.jpeg' and fn[-4:] != '.xls' and fn[-4:] != '.GIF' and fn[-4:] != '.JPG' and fn[-5:] != '.JPEG' and fn[-4:] != '.XLS' and fn[-4:] != '.PNG' and fn[-4:] != '.png' and fn[-4:] != '.cab' and fn[-4:] != '.CAB' and fn[-4:] != '.ico':
		notepad.open(root + "\\" + fn)
		console.write(root + "\\" + fn + "\r\n")
		notepad.runMenuCommand("Encoding", "Convert to UTF-8 without BOM")
		notepad.save()
		notepad.close()

I think the code speaks for itself, just be 100% sure that you do the conversion to UTF-8 without the UTF-8 byte order mark (BOM) since javac does not support this special character.

If you have problems running the script, then first open the console (Plugins > Python Script > Show Console). Chances are that the indents got messed up (for those who don’t know Python, it doesn’t use curly brackets to identify a code block, it uses correct indentation instead).

oqt-maven-plugin v0.3 release

Today I’ve release the oqt-maven-plugin v0.3 to the Sonatype OSS.

Not much has changed since v0.2, I’ve just fixed 2 bugs and made very few other improvements. All info can be found on http://oqt.sourceforge.net/ .

In earlier versions I’ve stated that you could add the plugin to the module containing the entities but this is in fact not working; for some reason the freshly compiled classes in the target won’t find their way to the classpath.
I’m still thinking about a solution for this, but for the moment it’s best to put the oqt-maven-plugin in your EJB-JAR.