Distributed transaction using spring and spring jdbctemplate - spring

I have two datasource with two schema in oracle, I am performing unittest but it is failing. I want if the second transaction failed then it should rollback the first trasaction. Below is my code.
package com.test.db;
import static org.junit.Assert.assertEquals;
import java.util.Date;
import javax.sql.DataSource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.AfterTransaction;
import org.springframework.test.context.transaction.BeforeTransaction;
import org.springframework.transaction.annotation.Transactional;
//#EnableTransactionManagement
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations="/META-INF/spring/data-source-context.xml")
public class MultipleDatasourceTests {
private JdbcTemplate jdbcTemplate;
private JdbcTemplate otherJdbcTemplate;
#Autowired
public void setDataSources(#Qualifier("dataSource") DataSource dataSource,
#Qualifier("otherDataSource") DataSource otherDataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
this.otherJdbcTemplate = new JdbcTemplate(otherDataSource);
}
#BeforeTransaction
public void clearData() {
jdbcTemplate.update("delete from T_ORACLE1");
otherJdbcTemplate.update("delete from T_ORACLE1");
}
#AfterTransaction
public void checkPostConditions() {
int count = jdbcTemplate.queryForInt("select count(*) from T_ORACLE1");
// This change was rolled back by the test framework
assertEquals(0, count);
count = otherJdbcTemplate.queryForInt("select count(*) from T_ORACLE1");
// This rolls back as well if the connections are managed together
assertEquals(0, count);
}
/**
* Vanilla test case for two inserts into two data sources. Both should roll
* back.
*
* #throws Exception
*/
#Transactional
#Test
public void testInsertIntoTwoDataSources() throws Exception {
jdbcTemplate.update("delete from T_ORACLE1");
otherJdbcTemplate.update("delete from T_ORACLE2");
int count = jdbcTemplate.update(
"INSERT into T_ORACLE1 (id,name,foo_date) values (?,?,null)", 0,
"foo");
assertEquals(1, count);
count = otherJdbcTemplate
.update(
"INSERT into T_ORACLE2 (id,operation,name,audit_date) values (?,?,?,?)",
1, "INSERT", "foo", new Date());
assertEquals(1, count);
}
/**
* Shows how to check the operation on the inner data source to see if it
* has already been committed, and if it has do something different, instead
* of just hitting a {#link DataIntegrityViolationException}.
*
* #throws Exception
*/
#Transactional
#Test
public void testInsertWithCheckForDuplicates() throws Exception {
int count = jdbcTemplate.update(
"INSERT into T_ORACLE1 (id,name,foo_date) values (?,?,null)", 0,
"foo");
assertEquals(1, count);
count = otherJdbcTemplate.update(
"UPDATE T_ORACLE2 set operation=?, name=?, audit_date=? where id=?",
"UPDATE", "foo", new Date(), 0);
if (count == 0) {
count = otherJdbcTemplate.update(
"INSERT into T_ORACLE2 (id,operation,name,audit_date) values (?,?,?,?)",
0, "INSERT", "foo", new Date());
}
assertEquals(1, count);
}
}
XML
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<context:annotation-config />
<context:component-scan base-package="com.test.*"/>
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" />
<property name="url" value="jdbc:oracle:thin:#//localhost:1521/TESTDB1" />
<property name="username" value="ORACLE1"/>
<property name="password" value="ORACLE1"/>
</bean>
<bean id="otherDataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" />
<property name="url" value="jdbc:oracle:thin:#//localhost:1521/TESTDB2" />
<property name="username" value="ORACLE2"/>
<property name="password" value="ORACLE2"/>
</bean>
<bean id="transactionManager" class="com.test.db.MultiTransactionManager">
<property name="transactionManagers">
<list>
<bean
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<bean
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="otherDataSource" />
</bean>
</list>
</property>
</bean>
</beans>
package com.test.db;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionException;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.AbstractPlatformTransactionManager;
import org.springframework.transaction.support.DefaultTransactionStatus;
public class MultiTransactionManager extends
AbstractPlatformTransactionManager {
private List<PlatformTransactionManager> transactionManagers = new ArrayList<PlatformTransactionManager>();
private ArrayList<PlatformTransactionManager> reversed;
public void setTransactionManagers(
List<PlatformTransactionManager> transactionManagers) {
this.transactionManagers = transactionManagers;
reversed = new ArrayList<PlatformTransactionManager>(
transactionManagers);
Collections.reverse(reversed);
}
#Override
protected void doBegin(Object transaction, TransactionDefinition definition)
throws TransactionException {
#SuppressWarnings("unchecked")
List<DefaultTransactionStatus> list = (List<DefaultTransactionStatus>) transaction;
for (PlatformTransactionManager transactionManager : transactionManagers) {
DefaultTransactionStatus element = (DefaultTransactionStatus) transactionManager
.getTransaction(definition);
list.add(0, element);
}
}
#Override
protected void doCommit(DefaultTransactionStatus status)
throws TransactionException {
#SuppressWarnings("unchecked")
List<DefaultTransactionStatus> list = (List<DefaultTransactionStatus>) status
.getTransaction();
int i = 0;
for (PlatformTransactionManager transactionManager : reversed) {
TransactionStatus local = list.get(i++);
try {
transactionManager.commit(local);
} catch (TransactionException e) {
logger.error("Error in commit", e);
// Rollback will ensue as long as rollbackOnCommitFailure=true
throw e;
}
}
}
#Override
protected Object doGetTransaction() throws TransactionException {
return new ArrayList<DefaultTransactionStatus>();
}
#Override
protected void doRollback(DefaultTransactionStatus status)
throws TransactionException {
#SuppressWarnings("unchecked")
List<DefaultTransactionStatus> list = (List<DefaultTransactionStatus>) status
.getTransaction();
int i = 0;
TransactionException lastException = null;
for (PlatformTransactionManager transactionManager : reversed) {
TransactionStatus local = list.get(i++);
try {
transactionManager.rollback(local);
} catch (TransactionException e) {
// Log exception and try to complete rollback
lastException = e;
logger.error("Error in rollback", e);
}
}
if (lastException!=null) {
throw lastException;
}
}
}
My pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.springsource.open</groupId>
<artifactId>spring-best-db-db</artifactId>
<version>2.0.0.CI-SNAPSHOT</version>
<packaging>jar</packaging>
<name>Spring Best Efforts DB-DB</name>
<description><![CDATA[Sample project showing multi DataSource transaction
processing with Spring using best efforts 1PC.
]]> </description>
<properties>
<maven.test.failure.ignore>true</maven.test.failure.ignore>
<spring.framework.version>4.1.4.RELEASE</spring.framework.version>
</properties>
<profiles>
<profile>
<id>strict</id>
<properties>
<maven.test.failure.ignore>false</maven.test.failure.ignore>
</properties>
</profile>
<profile>
<id>fast</id>
<properties>
<maven.test.skip>true</maven.test.skip>
</properties>
</profile>
</profiles>
<dependencies>
<dependency>
<groupId>hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<version>1.8.0.7</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>1.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1</version>
<exclusions>
<exclusion>
<groupId>avalon-framework</groupId>
<artifactId>avalon-framework</artifactId>
</exclusion>
<exclusion>
<groupId>logkit</groupId>
<artifactId>logkit</artifactId>
</exclusion>
<exclusion>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.derby</groupId>
<artifactId>derby</artifactId>
<version>10.2.1.6</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.9</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.easymock</groupId>
<artifactId>easymock</artifactId>
<version>2.4</version>
<scope>test</scope>
</dependency>
<!-- Spring Dependencies -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.framework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.framework.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.framework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.framework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.framework.version}</version>
</dependency>
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc8</artifactId>
<version>12.1.0.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.github.marcus-nl.btm/btm-spring -->
<dependency>
<groupId>com.github.marcus-nl.btm</groupId>
<artifactId>btm-spring</artifactId>
<version>3.0.0-mk1</version>
</dependency>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>6.0</version>
</dependency>
</dependencies>
</project>
TABLE IN ORACLE1 database
create table T_ORACLE1 (
id integer not null primary key,
name varchar(80),
foo_date timestamp
);
Table in ORACLE2 database
create table T_ORACLE2 (
id integer not null primary key,
operation varchar(20),
name varchar(80),
audit_date timestamp
);
I googled to use BitronixTransactionManager but, did not get any clue how to configure for two datasource.
It seems we can use hibernate but, I don't want to use hibernate. I want either jdbctemplate with sql query or simple jdbc.
The error I am getting is
java.lang.IllegalStateException: Cannot activate transaction synchronization - already active
at org.springframework.transaction.support.TransactionSynchronizationManager.initSynchronization(TransactionSynchronizationManager.java:270)
I am new to distributed transaction. Could you please help me on this? Is it possible to use two datasource but one transactionmanager?

You have to use XA datasources if you want to achieve distributed transactions and the transaction manager has to support XA transactions as well.
Using the Bitronix transaction manager should do it, but you have to use XA datasource as well: an Oracle-based implementation seems to be available in Oracle's JDBC driver (cf. https://docs.oracle.com/cd/E17904_01/web.1111/e13731/thirdpartytx.htm#WLJTA266).
You can find an example of Spring configuration for Bitronix here: https://www.snip2code.com/Snippet/652599/Example-distributed-XA-transaction-confi/, just make sure to adjust the datasources properties to use oracle.jdbc.xa.client.OracleXADataSource instead of the PostgreSQL one.
Note however that XA/distributed transactions are no silver bullet and won't be able to cope some classes of problem (e.g. network failures); you should really think to possible alternatives before taking that road.

Related

Error creating bean 'org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#1'

I'm trying to create a web-app, using Maven and Intellij Idea. Tests work fine as installing in .war-file. But when I try to refer to my rest with jetty, I have lots of error cases:
Error creating bean with name 'org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#1' defined in ServletContext resource [/WEB-INF/rest-spring.xml]: Initialization of bean failed;
nested exception is java.lang.NoClassDefFoundError: com/fasterxml/jackson/core/JsonProcessingException
Here are rest module files:
Web.xml
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.5"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<servlet>
<servlet-name>restDispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--the location of the spring context configuration file-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/rest-spring.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>restDispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
rest-spring.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:jdbc="http://www.springframework.org/schema/jdbc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/jdbc
http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<mvc:annotation-driven/>
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:model.properties</value>
<value>classpath:database.properties</value>
<value>classpath:automobile.properties</value>
</list>
</property>
</bean>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<jdbc:initialize-database data-source="dataSource">
<jdbc:script location="classpath*:create-tables-model.sql"/>
<jdbc:script location="classpath*:create-tables-automobile.sql"/>
<jdbc:script location="classpath*:data-script.sql"/>
</jdbc:initialize-database>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="messageConverters">
<list>
<ref bean="jsonConverter"/>
</list>
</property>
</bean>
<bean id="jsonConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="supportedMediaTypes" value="application/json" />
<property name="prettyPrint" value="true" />
</bean>
<bean id="modelDao" class="com.dao.ModelDaoImpl">
<constructor-arg ref="dataSource" />
</bean>
<bean id="automobileDao" class="com.dao.AutomobileDaoImpl">
<constructor-arg ref="dataSource" />
</bean>
<bean id="modelService" class="com.service.ModelServiceImpl">
<property name = "modelDao" ref = "modelDao"/>
</bean>
<bean id="automobileService" class="com.service.AutomobileServiceImpl">
<property name = "automobileDao" ref = "automobileDao"/>
</bean>
<context:component-scan base-package="com.rest"/>
</beans>
ModelRestController
package com.rest;
import com.dto.ModelDto;
import com.general.Model;
import com.service.ModelService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.List;
/**
* Created by kohtpojiep on 23.01.16.
*/
#RestController
public class ModelRestController
{
private static final Logger LOGGER = LogManager.getLogger();
#Autowired
private ModelService modelService;
#RequestMapping(value="/models", method = RequestMethod.GET)
public #ResponseBody List<Model> getAllModels()
{
LOGGER.debug("Getting all models");
return modelService.getAllModels();
}
#RequestMapping(value="/model", method = RequestMethod.POST)
#ResponseStatus(value = HttpStatus.CREATED)
public #ResponseBody Integer addModel(#RequestBody Model model)
{
LOGGER.debug("Adding model modelName = {}", model.getModelName());
return modelService.addModel(model);
}
#RequestMapping (value="/model/{modelId}/{modelName}", method=RequestMethod.PUT)
#ResponseStatus(value = HttpStatus.ACCEPTED)
public #ResponseBody void updateModel(#PathVariable(value="modelId") Integer modelId,
#PathVariable(value="modelName") String modelName)
{
LOGGER.debug("Updating model modelId = {}", modelId);
modelService.updateModel(new Model(modelId,modelName));
}
#RequestMapping (value="/model/{modelName}", method = RequestMethod.DELETE)
#ResponseStatus(value = HttpStatus.NO_CONTENT)
public #ResponseBody void deleteModelByName(#PathVariable(value = "modelName") String modelName)
{
LOGGER.debug("Deleting model modelName= {}",modelName);
modelService.deleteModelByName(modelName);
}
#RequestMapping (value="/modelsdto", method = RequestMethod.GET)
#ResponseStatus(value = HttpStatus.OK)
public #ResponseBody
ModelDto getModelsDto()
{
LOGGER.debug("Getting models Dto");
return modelService.getModelDto();
}
}
AutomobileRestController
package com.rest;
import com.dto.AutomobileDto;
import com.general.Automobile;
import com.service.AutomobileService;
import org.joda.time.LocalDate;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.List;
/**
* Created by kohtpojiep on 02.02.16.
*/
#RestController
public class AutomobileRestController
{
private static final Logger LOGGER = LogManager.getLogger();
#Autowired
AutomobileService automobileService;
private static LocalDate convertToLocalDate(String date)
{
DateTimeFormatter formattedDate = DateTimeFormat.forPattern("dd/MM/yyyy");
return formattedDate.parseLocalDate(date);
}
#RequestMapping(value = "/automobiles", method = RequestMethod.GET)
#ResponseStatus(value = HttpStatus.ACCEPTED)
public #ResponseBody List<Automobile> getAllAutomobiles()
{
LOGGER.debug("Getting all automobiles");
return automobileService.getAllAutomobiles();
}
#RequestMapping(value = "/automobile", method = RequestMethod.POST)
#ResponseStatus(value = HttpStatus.CREATED)
public #ResponseBody Integer addAutomobile (#RequestBody Automobile automobile)
{
LOGGER.debug("Adding automobile modelName = {}",automobile.getModelName());
return automobileService.addAutomobile(automobile);
}
#RequestMapping(value = "/automobile/update", method = RequestMethod.PUT)
#ResponseStatus(value = HttpStatus.ACCEPTED)
public #ResponseBody void updateAutomobile (#RequestBody Automobile automobile)
{
LOGGER.debug("Updating automobile automobileId = {}", automobile.getAutomobileId());
automobileService.updateAutomobile(automobile);
}
#RequestMapping(value = "/automobile/{automobileId}", method = RequestMethod.DELETE)
#ResponseStatus(value = HttpStatus.NO_CONTENT)
public #ResponseBody void depeteAutomobile (#PathVariable (value="automobileId")
Integer automobileId)
{
LOGGER.debug("Deleting automobile automobileId = {}",automobileId);
automobileService.deleteAutomobileById(automobileId);
}
#RequestMapping(value = "/automobiles/date-sort", method = RequestMethod.GET)
#ResponseStatus(value = HttpStatus.ACCEPTED)
public #ResponseBody List<Automobile> getAutomobilesSortedByDate (#RequestParam(value="firstDate")
String firstDate, #RequestParam (value="lastDate") String lastDate)
{
LOGGER.debug("Getting automobiles sorted by date:\n");
return automobileService.getAutomobilesSortedByDate(
convertToLocalDate(firstDate),convertToLocalDate(lastDate));
}
#RequestMapping(value = "/automobilesdto", method = RequestMethod.GET)
#ResponseStatus(value = HttpStatus.OK)
public #ResponseBody
AutomobileDto getAutomobileDto()
{
LOGGER.debug("Getting automobile DTO");
return automobileService.getAutomobileDto();
}
}
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>usermanagement</artifactId>
<groupId>com.epam.brest.course2015</groupId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>app-rest</artifactId>
<name>${project.artifactId}</name>
<packaging>war</packaging>
<dependencies>
<dependency>
<groupId>com.epam.brest.course2015</groupId>
<artifactId>app-service</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>org.easymock</groupId>
<artifactId>easymock</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>8.1.16.v20140903</version>
<configuration>
<stopPort>9091</stopPort>
<stopKey>STOP</stopKey>
<webAppConfig>
<contextPath>/rest</contextPath>
<allowDuplicateFragmentNames>true</allowDuplicateFragmentNames>
</webAppConfig>
<scanIntervalSeconds>10</scanIntervalSeconds>
<connectors>
<connector implementation="org.eclipse.jetty.server.nio.SelectChannelConnector">
<port>8081</port>
</connector>
</connectors>
</configuration>
</plugin>
</plugins>
</build>
</project>
I saw solutions for similar situations: people offer to change versions of used frameworks and up the search level of component scan, but it doesn't work for me.
Add dependency to your POM:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
</dependency>
As you use dependency management in parent POM, there is no need to specify version.
Solved! The problem was I use not only jackson-core dependency, but also jackson databind, datatype and annotations and these dependencies had different versions: 2.7.1 for "core" and 2.4.3 for other ones. Now I use the same version for all of them and thus adding dependency had an affect. Thx for your help!)
I had the same problem. I used spring version 5.0.5.RELEASE and Jackson Core version 2.4.3.
I upgraded Jackson core up to 2.9.5 and it works now : exception "Error creating bean with name 'org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter' " dissapeared and my rest service works.
Before in my pom.xml :
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.4.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.4.3</version>
</dependency>
After :
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.5</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.5</version>
</dependency>
I hope it will help.
Yes. Using version 2.9.5 works:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.5</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.5</version>
<exclusions>
<exclusion>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
</exclusion>
<exclusion>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</exclusion>
</exclusions>
</dependency>
I'm using spring-webmvc: 5.0.12.RELEASE, if that helps.

The object doesn't get persisted in Hibernate 4

I configured Hibernate and Spring.
The config and Java class:
package com.dao.impl;
import com.dao.IPersonRepository;
import com.dao.impl.generic.GenericRepository;
import com.model.Person;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
/**
* Created by ramezanimatin on 8/18/2015.
*/
#Repository
public class PersonRepository extends GenericRepository<Person, Long> implements IPersonRepository {
}
package com.dao;
import com.dao.iGeneric.IGenericRepository;
import com.model.Person;
/**
* Created by ramezanimatin on 8/18/2015.
*/
public interface IPersonRepository extends IGenericRepository<Person, Long> {
}
package com.dao.iGeneric;
import java.io.Serializable;
/**
* Created by ramezanimatin on 8/23/2015.
*/
public abstract interface IGenericRepository<T, pk extends Serializable> {
public void save(T t);
public T loadByEntityId(pk id);
}
package com.dao.impl.generic;
import com.dao.iGeneric.IGenericRepository;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import java.io.Serializable;
/**
* Created by ramezanimatin on 8/23/2015.
*/
#Repository
public class GenericRepository<T, pk extends Serializable> implements IGenericRepository<T, pk> {
#Autowired(required = true)
private SessionFactory sessionFactory;
private Session getSession() {
try {
return sessionFactory.getCurrentSession();
} catch (Exception e) {
return sessionFactory.openSession();
}
}
#Override
public void save(Object o) {
getSession().save(o);
}
#Override
public T loadByEntityId(pk id) {
Session session = getSession();
Query query = session.createQuery("from Person e where e.id=:id");
query.setParameter("id", id);
return ((T) query.uniqueResult());
}
}
xml config:
<?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:tx="http://www.springframework.org/schema/tx" xmlns:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd">
<context:property-placeholder location="classpath:oracle-hibernate.properties"/>
<context:component-scan base-package="com"/>
<!-- <jee:jndi-lookup id="dataSource" jndi-name="test" />-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/test"/>
<property name="username" value="root"/>
<property name="password" value="1234"/>
</bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="packagesToScan" value="com.model"/>
<property name="hibernateProperties">
<props>
<prop key="hibernate.hbm2ddl.auto">create</prop>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.show_sql"> ${hibernate.show_sql}</prop>
<prop key="hibernate.default_schema"> ${hibernate.default_schema}</prop>
<prop key="hibernate.connection.defaultNChar">true</prop> <!--Oracle need -->
<prop key="hibernate.connection.characterEncoding">UTF8</prop> <!--Oracle need -->
<prop key="hibernate.connection.charSet">UTF8</prop> <!--Oracle need -->
<prop key="hibernate.connection.useEncoding">true</prop> <!--Oracle need -->
<prop key="hibernate.transaction.factory_class" >${hibernate.transaction.factory_class}</prop>
<!-- <prop key="hibernate.transaction.manager_lookup_class" >${hibernate.transaction.manager_lookup_class}</prop>
<prop key="hibernate.current_session_context_class" >${hibernate.current_session_context_class}</prop>-->
</props>
</property>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"
proxy-target-class="true"/>
<bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
</beans>
personService:
package com.service.impl;
import com.dao.IPersonRepository;
import com.model.Person;
import com.service.IPersonService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* Created by ramezanimatin on 8/15/2015.
*/
#Service
public class PersonService implements IPersonService {
#Autowired(required = true)
private IPersonRepository iPersonRepository;
#Override
#Transactional
public void insertPerson(Person person) {
iPersonRepository.save(person);
}
#Override
#Transactional
public void loadByEntityId(Long id) {
Person person = iPersonRepository.loadByEntityId(id);
System.out.println(person.getName());
person.setName("matin");
}
}
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.test</groupId>
<artifactId>test</artifactId>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.0.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>4.0.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.0.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.0.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.0.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>4.0.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-commons-annotations</artifactId>
<version>3.2.0.Final</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>4.0.7.RELEASE</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>4.1.2.Final</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.5.8</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>4.0.7.RELEASE</version>
</dependency>
<dependency>
<groupId>javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.12.0.GA</version>
</dependency>
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>javax.persistence</artifactId>
<version>2.1.0</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>javax.transaction</groupId>
<artifactId>jta</artifactId>
<version>1.1</version>
</dependency>
<!-- jpa lib-->
</dependencies>
<build>
<finalName>test</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
My problem:
In Hibernate the Object Model has 3 states:
1- Transient
2- Persistent
3- Detached
I want to clarify the following situation:
When Object Model is fetched from the DataBase, it is Persistent. In loadByEntityId of PersonService when the Person Entity is loaded, It isn't persistent. Why?
I changed name to 'Matin' but didn't commited to DataBase.
Updated:
Thanks to M. Deinum for explain subtle differences.
Please remove this method:
private Session getSession() {
try {
return sessionFactory.getCurrentSession();
} catch (Exception e) {
return sessionFactory.openSession();
}
}
and just invoke getCurrentSession() for example:
Session session = sessionFactory.getCurrentSession();
In this method:
#Override
#Transactional
public void loadByEntityId(Long id) {
Person person = iPersonRepository.loadByEntityId(id);
System.out.println(person.getName());
person.setName("matin");
}
you have found Person entity and it's pretty much done. Under the hood you are using session in Hibernate so this particular code:
Session session = sessionFactory.getCurrentSession();
Query query = session.createQuery("from Person e where e.id=:id");
query.setParameter("id", id);
return ((T) query.uniqueResult());
only retrieve data.
What you need to do is define update method in your repository IGenericRepository interface:
public abstract interface IGenericRepository<T, pk extends Serializable> {
public void save(T t);
public T loadByEntityId(pk id);
public void updateEntity(T t);
}
implement it in GenericRepository:
#Override
public void updateEntity(T entity) {
getSession().update(entity);
}
define in IPersonService method updatePerson and implement it in PersonService and use updateEntity from Repostitory:
#Override
#Transactional
public void updatePerson(Person person) {
iPersonRepository.updateEntity(person);
}
And now when you use loadByEntityId after you have updated Person with updatePerson you should retrieve updated entity from db.

Custom exception mapping for JAX-RS Bean validation

I am building a REST web service using Apache CXF, Spring and JAX-RS, where I need to send custom exceptions when input JSON validation fails.
Instead of using business logic, I am trying to use Out-of-the-box bean validation feature of CXF, JAX-RS.
Bean validation works fine, however, it always throws 500 exception, which is less than useful. As per documentation, it is the expected behavior of org.apache.cxf.jaxrs.validation.ValidationExceptionMapper.
I am trying to extend exception mapper so that I can throw different error message, but this custom mapper is not getting called.
Below is my code setup ->
pom.xml
<!-- Spring Framework -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.1.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.1.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.1.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.1.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>4.1.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>3.2.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>3.2.7.RELEASE</version>
</dependency>
<!-- CXF -->
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-core</artifactId>
<version>3.0.2</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxrs</artifactId>
<version>3.0.2</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-bundle-jaxrs</artifactId>
<version>2.7.5</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-jaxrs</artifactId>
<version>1.9.3</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-xc</artifactId>
<version>1.9.3</version>
</dependency>
<!-- Hibernate validation -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.1.3.Final</version>
</dependency>
<!-- JSR 303 Validation framework -->
<dependency>
<groupId>org.glassfish.jersey.ext</groupId>
<artifactId>jersey-bean-validation</artifactId>
<version>2.19</version>
</dependency>
JAX-RS server
<jaxrs:server address="/" id="iamService">
<jaxrs:serviceBeans>
<bean class="com.XXXXXX.iam.service.core.endpoint.impl.IAMIdentityServiceImpl" />
</jaxrs:serviceBeans>
<jaxrs:inInterceptors>
<ref bean="validationInInterceptor" />
</jaxrs:inInterceptors>
<jaxrs:outInterceptors>
<ref bean="validationOutInterceptor" />
<ref bean="exceptionMapperOutInterceptor" />
</jaxrs:outInterceptors>
<jaxrs:extensionMappings>
<entry key="json" value="application/json" />
</jaxrs:extensionMappings>
<jaxrs:providers>
<ref bean='jsonProvider' />
<ref bean="ampfExceptionMapper"/>
</jaxrs:providers>
</jaxrs:server>
<bean id="ampfExceptionMapper" class="com.xxxxxxx.iam.service.core.interceptor.AmpfValidationExceptionMapper"/>
<bean id="exceptionMapperOutInterceptor" class="org.apache.cxf.jaxrs.interceptor.JAXRSOutExceptionMapperInterceptor"/>
<bean id="validationProvider" class="org.apache.cxf.validation.BeanValidationProvider" />
<bean id="validationInInterceptor" class="org.apache.cxf.jaxrs.validation.JAXRSBeanValidationInInterceptor">
<property name="provider" ref="validationProvider" />
</bean>
<bean id="validationOutInterceptor" class="org.apache.cxf.jaxrs.validation.JAXRSBeanValidationOutInterceptor">
<property name="provider" ref="validationProvider" />
</bean>
Custom Exception mapper
#Provider
#Produces(MediaType.APPLICATION_JSON)
public class AmpfValidationExceptionMapper implements ExceptionMapper<ConstraintViolationException>{
#Override
public Response toResponse(ConstraintViolationException exception) {
System.out.println("Executing Ameriprise Service exception mapping");
UniqueIDValidationResponse response = new UniqueIDValidationResponse();
response.setResponse("Invalid request");
return Response.ok(response).build();
}
}
Bean class
public class UniqueIDValidationRequest {
/*#Autowired
private Validator validator;*/
#XmlAttribute(name = "ssoId")
#NotBlank
#NotEmpty
#Length(max=20,min=5)
protected String ssoId;
#NotEmpty
#NotBlank
#XmlAttribute(name = "epHashId")
protected String epHashId;
This issue is resolved now. What I had to do is create a custom interceptors. This will catch the fault occurred while bean validation and add actual bean validation error instead. Please check the InInterceptor code below -->
#Provider
public class IamServInInterceptor extends
BeanValidationInInterceptor implements ContainerRequestFilter{
private static Logger logger = LoggerFactory.getLogger(IamServInInterceptor.class);
public IamServInInterceptor() {
logger.debug("IamServInInterceptor default consturctor called");
}
public IamServInInterceptor(String phase) {
super(phase);
logger.debug("IamServInInterceptor phase : " + phase);
}
#Override
public void filter(ContainerRequestContext arg0) throws IOException {
// TODO Auto-generated method stub
logger.debug("IamServInInterceptor filter is called");
InterceptorChain chain = PhaseInterceptorChain.getCurrentMessage()
.getInterceptorChain();
chain.add(this);
}
protected Object getServiceObject(Message message) {
logger.debug("IamServInInterceptor getServiceObject called");
return ValidationUtils.getResourceInstance(message);
}
protected void handleValidation(Message message, Object resourceInstance,
Method method, List<Object> arguments){
try {
logger.debug("IamServInInterceptor handleValidation called");
super.handleValidation(message, resourceInstance, method, arguments);
} catch (ConstraintViolationException ex) {
logger.debug("IamServInInterceptor handleValidation has some exception");
UniqueIDValidationResponse response = new UniqueIDValidationResponse();
response.setResponse("INPUT_VALIDATION_FAILED");
ConstraintViolationException constraintException = (ConstraintViolationException)ex;
Set<ConstraintViolation<?>> constraint=
constraintException.getConstraintViolations();
Iterator<ConstraintViolation<?>> iterator = constraint.iterator();
while(iterator.hasNext()){
ConstraintViolation<?> error = iterator.next();
logger.debug("Error : " + error.toString());
response.getMessage().add(error.getMessage());
}
ex.printStackTrace();
message.getExchange().put(Response.class, Response.ok(response).build());
}
}
}
I resolved with a exception mapper:
import org.apache.cxf.validation.ResponseConstraintViolationException;
import org.slf4j.LoggerFactory;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.ValidationException;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;
import java.util.Iterator;
/**
* Created by Hyun Woo Son on 1/9/18
**/
#Provider
public class ValidationExceptionMapperCustom implements ExceptionMapper<ValidationException> {
private static org.slf4j.Logger logger = LoggerFactory.getLogger(ValidationExceptionMapperCustom.class);
public Response toResponse(ValidationException exception) {
Response.Status errorStatus = Response.Status.INTERNAL_SERVER_ERROR;
ResponseVo responseVo = new ResponseVo();
if (exception instanceof ConstraintViolationException) {
ConstraintViolationException constraint = (ConstraintViolationException)exception;
Iterator i$ = constraint.getConstraintViolations().iterator();
StringBuilder errorMsg = new StringBuilder(RestExcepcionEnum.PARAMETROS_EXCEPCION.getValue());
errorMsg.append("\n");
while(i$.hasNext()) {
ConstraintViolation<?> violation = (ConstraintViolation)i$.next();
logger.info("error {}",violation.getRootBeanClass().getSimpleName() + "." + violation.getPropertyPath() + ": " + violation.getMessage());
errorMsg.append("La propiedad : "+violation.getPropertyPath() + " con error: " + violation.getMessage() );
errorMsg.append("\n");
}
if (!(constraint instanceof ResponseConstraintViolationException)) {
errorStatus = Response.Status.BAD_REQUEST;
}
responseVo.setMensaje(errorMsg.toString());
}
else{
responseVo.setMensaje("Error inesperado: "+exception.getMessage());
logger.debug("Error de validacion {}",exception.getMessage(),exception);
}
return Response.status(errorStatus).entity(responseVo).build();
}
}
And add it to the Application class:

Spring-boot+JPA EntityManager inject fails

In my J2EE application I try to use spring-boot and JPA technologies with injection of EntityManager into DAO layer. However, I have some problems... My repository for user CRUD:
#Repository
public class UserRepositoryImpl implements UserRepository {
#PersistenceContext(unitName = "data")
private EntityManager entityManager;
// and crud methods
}
My spring-boot application class:
#SpringBootApplication
public class App {
public static void main(String [] args) {
SpringApplication.run(App.class, args);
}
}
And finally my persistence.xml, located in src/main/resources/META-INF folder:
<?xml version="1.0" encoding="UTF-8"?>
<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">
<persistence-unit name="data" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<class>com.example.domain.User</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
<properties>
<property name="hibernate.connection.autocommit" value="false" />
<property name="hibernate.dialect" value="org.hibernate.dialect.SQLServerDialect" />
<property name="hibernate.c3p0.min_size" value="4" />
<property name="hibernate.c3p0.max_size" value="128" />
<property name="javax.persistence.jdbc.url" value="jdbc:sqlserver://localhost:1433;databaseName=qwerty;sendStringParametersAsUnicode=false" />
<property name="javax.persistence.jdbc.user" value="sa" />
<property name="javax.persistence.jdbc.driver" value="com.microsoft.sqlserver.jdbc.SQLServerDriver" />
<property name="javax.persistence.jdbc.password" value="" />
<property name="hibernate.hbm2ddl.auto" value="update" />
<property name="hibernate.show_sql" value="false" />
</properties>
</persistence-unit>
So, when I try using this injected entityManager I get NullPointerException. Other #Autowired fields are injected without any problems. What's wrong with this code? Do I need some extra configuration?
I am a beginner (not even a Junior developer) and I do have some misunderstanding of what Spring-boot is and how to configure it, like Spring in xml file. If such an xml configuration is needed due to inject EM, please, show how to do it.
upd2. Dependencies
<dependencies>
<!-- logger -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<!-- db -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-c3p0</artifactId>
</dependency>
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
</dependency>
<dependency>
<groupId>com.microsoft</groupId>
<artifactId>sqljdbc4</artifactId>
</dependency>
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.2.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.5.3</version>
</dependency>
<!-- csv -->
<dependency>
<groupId>com.opencsv</groupId>
<artifactId>opencsv</artifactId>
<version>3.3</version>
</dependency>
<!-- spring-boot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>1.2.4.RELEASE</version>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j</artifactId>
<version>1.2.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>4.1.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>1.2.4.RELEASE</version>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
You should use the dependency for spring-boot-starter-data-jpa
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
And to use a persistence xml you should define a bean as it says in the documentation.
Spring doesn’t require the use of XML to configure the JPA provider, and Spring Boot assumes you want to take advantage of that feature. If you prefer to use persistence.xml then you need to define your own #Bean of type LocalEntityManagerFactoryBean (with id ‘entityManagerFactory’, and set the persistence unit name there.
http://docs.spring.io/spring-boot/docs/current/reference/html/howto-data-access.html#howto-use-traditional-persistence-xml
Or you could skip the persistence.xml entirely and define connection properties in the application.properties file.
Quote from the documentation
DataSource configuration is controlled by external configuration properties in spring.datasource.*. For example, you might declare the following section in application.properties:
spring.datasource.url=jdbc:mysql://localhost/test
spring.datasource.username=dbuser
spring.datasource.password=dbpass
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
http://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-sql.html#boot-features-connect-to-production-database
(change the driver and other data to match your environment)
Good luck!
you can use java configuration in order to configure jpa persistence. The code bellow show you sample example of configuration:
#Component
public class JpaConfiguration {
#Bean
#Primary
public DataSource dataSource() {
final SimpleDriverDataSource dataSource = new SimpleDriverDataSource();
dataSource.setDriver(new org.postgresql.Driver());
dataSource.setUrl("jdbc:postgresql://localhost:5432/users");
dataSource.setUsername("postgres");
dataSource.setPassword("admin");
return dataSource;
}
#Bean
public JpaVendorAdapter jpaVendorAdapter() {
HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
jpaVendorAdapter.setGenerateDdl(true);
jpaVendorAdapter.setShowSql(true);
jpaVendorAdapter.setDatabasePlatform("org.hibernate.dialect.PostgreSQLDialect");
return jpaVendorAdapter;
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean lef = new LocalContainerEntityManagerFactoryBean();
lef.setPackagesToScan("tn.bergit.crud.entity");
lef.setDataSource(dataSource());
lef.setJpaVendorAdapter(jpaVendorAdapter());
Properties properties = new Properties();
properties.setProperty("hibernate.show_sql", "true");
properties.setProperty("hibernate.jdbc.fetch_size", "100");
properties.setProperty("hibernate.hbm2ddl.auto", "update");
lef.setJpaProperties(properties);
return lef;
}
}
You can see this example on github (click here)
The class from which you're asking for the EntityManager should be a Spring Bean if you're using the following
public class Foo {
#PersistenceContext
private EntityManager entityManager;
}
Here, if you're using new keyword to get an instance of Foo class above, then EntityManager will be null.
Hope this helps someone!
if you want to keep using persistence.xml file just add the below code in your configuration class
#Bean
public LocalEntityManagerFactoryBean entityManagerFactory(){
LocalEntityManagerFactoryBean factoryBean = new LocalEntityManagerFactoryBean();
factoryBean.setPersistenceUnitName("data");
return factoryBean;
}

Not able to run Dyamic FTP example- Spring integration

I am trying to run the Dynamic FTP example in Spring Integration. But changed the mode to SFTP. While running the test it is showing the expression evaluation is failed due to below message
org.springframework.beans.factory.xml.XmlBeanDefinitionStoreException: Line 30 in XML document from class path resource [META-INF/spring/integration/dynamic-ftp-outbound-adapter-context.xml] is invalid; nested exception is org.xml.sax.SAXParseException; lineNumber: 30; columnNumber: 31; cvc-complex-type.2.4.c: The matching wildcard is strict, but no declaration can be found for element 'int-sftp:outbound-channel-adapter'.
My files are as follow
**pom.xml**
<?xml version="1.0" encoding="UTF-8"?>
<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>
<groupId>org.springframework.integration.samples</groupId>
<artifactId>DynamicFtp</artifactId>
<version>0.0.1</version>
<name>Dynamic FTP </name>
<description>Dynamic FTP</description>
<!-- <repositories>
<repository>
<id>repo.spring.io.milestone</id>
<name>Spring Framework Maven Milestone Repository</name>
<url>https://repo.spring.io/libs-milestone</url>
</repository>
</repositories> -->
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-all</artifactId>
<version>1.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-sftp</artifactId>
<version>4.1.2.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-ftp</artifactId>
<version>4.1.0.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-core</artifactId>
<version>4.1.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.1.1.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>1.9.5</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
**DynamicFtpChannelResolver**
public class DynamicFtpChannelResolver {
//In production environment this value will be significantly higher
//This is just to demonstrate the concept of limiting the max number of
//Dynamically created application contexts we'll hold in memory when we execute
//the code from a junit
public static final int MAX_CACHE_SIZE = 2;
private final LinkedHashMap<String, MessageChannel> channels =
new LinkedHashMap<String, MessageChannel>() {
private static final long serialVersionUID = 1L;
#Override
protected boolean removeEldestEntry(
Entry<String, MessageChannel> eldest) {
//This returning true means the least recently used
//channel and its application context will be closed and removed
boolean remove = size() > MAX_CACHE_SIZE;
if(remove) {
MessageChannel channel = eldest.getValue();
ConfigurableApplicationContext ctx = contexts.get(channel);
if(ctx != null) { //shouldn't be null ideally
ctx.close();
contexts.remove(channel);
}
}
return remove;
}
};
private final Map<MessageChannel, ConfigurableApplicationContext> contexts =
new HashMap<MessageChannel, ConfigurableApplicationContext>();
/**
* Resolve a customer to a channel, where each customer gets a private
* application context and the channel is the inbound channel to that
* application context.
*
* #param customer
* #return a channel
*/
public MessageChannel resolve(String customer) {
MessageChannel channel = this.channels.get(customer);
if (channel == null) {
channel = createNewCustomerChannel(customer);
}
return channel;
}
private synchronized MessageChannel createNewCustomerChannel(String customer) {
MessageChannel channel = this.channels.get(customer);
if (channel == null) {
ConfigurableApplicationContext ctx = new ClassPathXmlApplicationContext(
new String[] { "/META-INF/spring/integration/dynamic-ftp-outbound-adapter-context.xml" },
false);
this.setEnvironmentForCustomer(ctx, customer);
ctx.refresh();
channel = ctx.getBean("toFtpChannel", MessageChannel.class);
this.channels.put(customer, channel);
//Will works as the same reference is presented always
this.contexts.put(channel, ctx);
}
return channel;
}
/**
* Use Spring 3.1. environment support to set properties for the
* customer-specific application context.
*
* #param ctx
* #param customer
*/
private void setEnvironmentForCustomer(ConfigurableApplicationContext ctx,
String customer) {
StandardEnvironment env = new StandardEnvironment();
Properties props = new Properties();
// populate properties for customer
// props.setProperty("host", "host.for." + customer);
// props.setProperty("user", "user");
// props.setProperty("password", "password");
props.setProperty("host", "172.18.554.23");
props.setProperty("user", "iuser");
props.setProperty("port", "22");
props.setProperty("password", "ipwddd");
props.setProperty("remote.directory", "/u01/dpp/dppuser/ftp/");
PropertiesPropertySource pps = new PropertiesPropertySource("ftpprops", props);
env.getPropertySources().addLast(pps);
ctx.setEnvironment(env);
System.out.println("the properties are set" +pps);
}
}
**dynamic-ftp-outbound-context.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:int="http://www.springframework.org/schema/integration"
xmlns:int-ftp="http://www.springframework.org/schema/integration/ftp"
xmlns:int-sftp="http://www.springframework.org/schema/integration /sftp"
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/integration http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/integration/ftp http://www.springframework.org/schema/integration/ftp/spring-integration-ftp.xsd
http://www.springframework.org/schema/integration/sftp http://www.springframework.org/schema/integration/ftp/spring-integration-sftp.xsd">
<context:property-placeholder />
<bean id="ftpClientFactory" class="org.springframework.integration.sftp.session.DefaultSftpSessionFactory">
<property name="host" value="${host}"/>
<property name="port" value="${port}"/>
<property name="username" value="${user}"/>
<property name="password" value="${password}"/>
</bean>
<int:channel id="toFtpChannel"/>
<int-sftp:outbound-channel-adapter id="ftpOutbound"
channel="toFtpChannel"
remote-directory="${remote.directory}"
session-factory="sftpSessionFactory"
mode="REPLACE"
remote-file-separator="/">
<int:poller fixed-rate="100" time-unit="MILLISECONDS" max-messages-per-poll="100"/>
</int-sftp:outbound-channel-adapter>
<bean id="sftpSessionFactory"
class="org.springframework.integration.file.remote.session.CachingSessionFactory">
<constructor-arg ref="ftpClientFactory" />
</bean>
</beans>
**dyamic-ftp-ountbound-sample-context.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:int="http://www.springframework.org/schema/integration"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
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="channelResolver" class="com.tcs.DynamicFtp.DynamicFtpChannelResolver" />
<int:channel id="toDynRouter" />
<int:router input-channel="toDynRouter"
expression="#channelResolver.resolve(headers['customer'])"/>
</beans>
**The test file**
import static org.hamcrest.Matchers.instanceOf;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.net.UnknownHostException;
import org.junit.Test;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessagingException;
public class FtpOutboundChannelAdapterSample {
#Test
public void runDemo() throws Exception{
ConfigurableApplicationContext ctx =
new ClassPathXmlApplicationContext("META-INF/spring/integration /DynamicFtpOutboundChannelAdapterSample-context.xml");
MessageChannel channel = ctx.getBean("toDynRouter", MessageChannel.class);
File file = File.createTempFile("temp", "txt");
BufferedWriter bw = new BufferedWriter(new FileWriter(file));
bw.write("This is the temporary file content");
bw.close();
Message<File> message = MessageBuilder.withPayload(file)
.setHeader("customer", "cust1")
.build();
try {
channel.send(message);
}
catch (MessagingException e) {
assertThat(e.getCause().getCause().getCause(), instanceOf(UnknownHostException.class));
assertTrue(e.getCause().getCause().getCause().getMessage().startsWith("host.for.cust1"));
}
// send another so we can see in the log we don't create the ac again.
try {
channel.send(message);
}
catch (MessagingException e) {
assertThat(e.getCause().getCause().getCause(), instanceOf(UnknownHostException.class));
assertTrue(e.getCause().getCause().getCause().getMessage().startsWith("host.for.cust1"));
}
// send to a different customer; again, check the log to see a new ac is built
message = MessageBuilder.withPayload(file)
.setHeader("customer", "cust2").build();
try {
channel.send(message);
}
catch (MessagingException e) {
assertThat(e.getCause().getCause().getCause(), instanceOf(UnknownHostException.class));
assertTrue(e.getCause().getCause().getCause().getMessage().startsWith("host.for.cust2"));
}
// send to a different customer; again, check the log to see a new ac is built
//and the first one created (cust1) should be closed and removed as per the max cache size restriction
message = MessageBuilder.withPayload(file)
.setHeader("customer", "cust3").build();
try {
channel.send(message);
}
catch (MessagingException e) {
assertThat(e.getCause().getCause().getCause(), instanceOf(UnknownHostException.class));
assertTrue(e.getCause().getCause().getCause().getMessage().startsWith("host.for.cust3"));
}
//send to cust1 again, since this one has been invalidated before, we should
//see a new ac created (with ac of cust2 destroyed and removed)
message = MessageBuilder.withPayload(file)
.setHeader("customer", "cust1").build();
try {
channel.send(message);
}
catch (MessagingException e) {
assertThat(e.getCause().getCause().getCause(),
instanceOf(UnknownHostException.class));
assertEquals("host.for.cust1",
e.getCause().getCause().getCause().getMessage());
}
ctx.close();
}
}
You have an error in your xmlns configuration...
xmlns:int-sftp="http://www.springframework.org/schema/integration /sftp"
there should be no spaces in it.
EDIT:
You have another error:
http://www.springframework.org/schema/integration/ftp/spring-integration-sftp.xsd">
should be
http://www.springframework.org/schema/integration/sftp/spring-integration-sftp.xsd">
I generally use an IDE to manage the namespace stuff; doing it manually is error-prone, as you have found.
I just tested it locally with no problems. However, this
<property name="username" value="${user}"/>
should have failed because the property is user on the sftp factory; also why did you add a <poller/>?
The channel is not pollable.

Resources