Custom exception mapping for JAX-RS Bean validation - spring

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:

Related

Distributed transaction using spring and spring jdbctemplate

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.

Request method of type post not recognized by Spring Rest Api

This Code work fine :
#RestController
#RequestMapping("/api/post")
public class PostController {
#Autowired
private PostService postService;
public PostService getPostService() {
return postService;
}
public void setPostService(PostService postService) {
this.postService = postService;
}
#RequestMapping(value = "/userPost", method = RequestMethod.GET)
#ResponseBody
public String userPost(String username) {
try {
List<Validation> validations = new ArrayList<Validation>();
ValidationHandeler.rejectIfEmptyOrWhitespace(validations, username, "username is requierd");
if (validations.isEmpty()) {
List<Post> followers = getPostService().findUserPost(username);
return new Message(followers, MessageSuccesStatusEnum.SUCCESS).toString();
}
return new Message(validations, MessageSuccesStatusEnum.FAILED).toString();
} catch (Exception ex) {
return new Message("Error retriving follower list : " + ex.toString(), MessageSuccesStatusEnum.FAILED)
.toString();
}
}
and this is the output :
{"result":"0","info":["username is requierd"]}
but when ever I change the RequestMethod type to post it give me the following output :
{"timestamp":1492948640973,"status":404,"error":"Not Found","message":"No message available","path":"/api/post/userPost"}
does any body know how to fix this problem?
Looks like you are using GET instead of POST as a request method.
#RequestMapping(value = "/userPost", method = RequestMethod.GET)
<dependencies>
<!-- https://mvnrepository.com/artifact/io.rest-assured/rest-assured -->
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<version>4.1.2</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.testng/testng -->
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>7.0.0</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/io.rest-assured/json-schema-validator -->
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>json-schema-validator</artifactId>
<version>4.1.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.10.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.xml.bind/jaxb-api -->
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
</dependency>
</dependencies>
TestClass TestNG
import org.testng.annotations.Test;
import io.restassured.RestAssured;
import static io.restassured.RestAssured.*;
import static io.restassured.RestAssured.given;
import static org.hamcrest.Matchers.equalTo;
import io.restassured.http.ContentType;
import io.restassured.response.Response;
public class basics {
#Test
public void getPlaceAPI()
{
RestAssured.baseURI = "https://maps.googleapis.com";
given().param("location", "42.3675294,-71.186966").param("radius",
"10000").param("key", "").when()
.get("/maps/api/place/textsearch/json").then().assertThat().statusCode(200);
}
}

Spring web app returns HTTP Status 406

I'm creating a basic spring based web app:
pom dependencies:
<properties>
<java-version>1.8</java-version>
<springframework-version>4.3.3.RELEASE</springframework-version>
<jackson-version>2.8.3</jackson-version>
<org.slf4j-version>1.7.6</org.slf4j-version>
<logback.version>1.1.7</logback.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${springframework-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${springframework-version}</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
<!-- Jackson JSON Mapper -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${jackson-version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson-version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>${jackson-version}</version>
</dependency>
<!-- Logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${org.slf4j-version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<!-- #Inject -->
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
</dependencies>
In order to skip the usage of web.xml I'm using WebApplicationInitializer:
public class AppInitializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
WebApplicationContext context = getContext();
servletContext.addListener(new ContextLoaderListener(context));
ServletRegistration.Dynamic dispatcher = servletContext.addServlet("DispatcherServlet", new DispatcherServlet(context));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/*");
}
private AnnotationConfigWebApplicationContext getContext() {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.setConfigLocation(SpringModule.class.getPackage().getName());
return context;
}
Here is my spring config class:
#Configuration
#ComponentScan(basePackages = "com.company.app")
public class SpringModule extends WebMvcConfigurerAdapter {
public SpringModule() {
super();
}
private MappingJackson2HttpMessageConverter customJackson2HttpMessageConverter() {
MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter();
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
jsonConverter.setObjectMapper(objectMapper);
return jsonConverter;
}
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
messageConverters.add(customJackson2HttpMessageConverter());
messageConverters.add(new Jaxb2RootElementHttpMessageConverter());
messageConverters.add(new StringHttpMessageConverter());
messageConverters.add(new ByteArrayHttpMessageConverter());
messageConverters.add(new ResourceHttpMessageConverter());
messageConverters.add(new SourceHttpMessageConverter());
messageConverters.add(new FormHttpMessageConverter());
super.configureMessageConverters(messageConverters);
}
/*
* Configure ContentNegotiationManager
*/
#Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.ignoreAcceptHeader(true).defaultContentType(
MediaType.APPLICATION_JSON);
}
Here is my test controller:
#Controller
#RequestMapping(value = "/user")
public class SomeController {
#RequestMapping(value = "/", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
public Person helloElad() {
return new Person("some name");
}
}
When testing the controller (using browser) I'm getting:
If I'm returning a plain String it works fine.
I tries to debug method configureMessageConverters and configureContentNegotiation but for some reason it never gets there (on bootstrapping), I'm not sure it is related to the problem though.
Any ideas?
HTTP 406 is an indication that your request content is not negotiated, it is probably that necessary http message converters are not found in configuration. Simple way to add basic set of message converter would be annotating your controller #EnableMvc
I had the same problem and couldn't find a solution here or elsewhere.
I also tried the advice of applying #EnableMvc on my AppConfig but that caused a different problem in which Tomcat wouldn't even successfully start up.
Eventually, I had to rewrite my AppInit class as follows:
https://github.com/viralpatel/spring4-restful-example/tree/master/src/main/java/net/viralpatel/spring/config
Now, I'm getting JSON back when I return a POJO. I don't like this fix. The code seems incomplete compared to the AppInit shown in the problem here, but I'm unstuck.

Bean validation not firing in my Spring Batch project with Spring version 4.1.7.RELEASE and Spring Batch version 3.0.4.RELEASE

Here is my test case, it is throwing the expected org.springframework.batch.item.validator.ValidationException. The commentor below helped and so did http://springbatchbyexample.blogspot.com/2014/09/validation-input-data.html
#Autowired
MdwValidatingItemProcessor mdwValidatingItemProcessor;
#Test(expected = ValidationException.class)
public void notNullOnProperty(){
MecMdw mecMdw = new MecMdw();
//nothing set, property "lob" is null
mdwValidatingItemProcessor.process(mecMdw);
}
I have the following domain object that is using javax.validation.constraints.NotNull:
import javax.validation.constraints.NotNull;
public class MecMdw {
#NotNull
private String lob;
The versions of Spring and Spring Batch are:
<spring.version>4.1.7.RELEASE</spring.version>
<spring.batch.version>3.0.4.RELEASE</spring.batch.version>
I have the following libraries added to my pom.xml:
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>4.0.2.GA</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.5.4</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.5.4</version>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.0.0.GA</version>
</dependency>
And in my Spring Batch job XML I have wired up the validator so:
<bean id="mdwValidatingItemProcessor" class="com.my.itemprocessor.MdwValidatingItemProcessor">
<property name="validator">
<bean class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean" />
</property>
</bean>
The MdwValidatingItemProcessor looks as follows, again for this test case, I am able to enter into the process method, while debugging:
public class MdwValidatingItemProcessor implements ItemProcessor<MecMdw, MecMdw> {
private Validator validator;
public void setValidator(Validator validator) {
this.validator = validator;
}
#Override
public MecMdw process(MecMdw item) throws Exception {
BindingResult results = BindAndValidate(item);
if (results.hasErrors())
buildValidationException(results);
//TODO write to kickout table
return item;
}
private BindingResult BindAndValidate(MecMdw item) {
DataBinder binder = new DataBinder(item);
binder.setValidator(validator);
binder.validate();
return binder.getBindingResult();
}
private void buildValidationException(BindingResult results) {
StringBuilder msg = new StringBuilder();
for (ObjectError error : results.getAllErrors()) {
msg.append("-*-*-*- \n" + error.toString() + "-*-*-*- \n");
}
throw new ValidationException(msg.toString());
}
This came courtsey of http://springbatchbyexample.blogspot.com/2014/09/validation-input-data.html

Spring data JPA configuration

I'm working on a spring web application using Spring Data JPA lately
I'm having problems with the persistanceConfiguration
#Configuration
#EnableTransactionManagement
#PropertySource({ "/resources/hibernate.properties" })
#EnableJpaRepositories(basePackages = "com.servmed.repositories")
public class PersistenceConfig {
#Autowired
private Environment env;
Properties jpaProperties() {
return new Properties() {
{
setProperty("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto"));
setProperty("hibernate.dialect", env.getProperty("hibernate.dialect")); //allows Hibernate to generate SQL optimized for a particular relational database.
setProperty("hibernate.show_sql",env.getProperty("hibernate.show_sql"));
}
};
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory()
{
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setGenerateDdl(true);
vendorAdapter.setShowSql(true);
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setDataSource(dataSource());
factory.setJpaVendorAdapter(vendorAdapter);
factory.setJpaProperties(jpaProperties());
factory.setPackagesToScan("com.servmed.models");
factory.setLoadTimeWeaver(new InstrumentationLoadTimeWeaver());
return factory;
}
#Bean
public PlatformTransactionManager transactionManager()
{
EntityManagerFactory factory = entityManagerFactory().getObject();
return new JpaTransactionManager(factory);
}
#Bean
public HibernateExceptionTranslator hibernateExceptionTranslator(){
return new HibernateExceptionTranslator();
}
#Bean
public DataSource dataSource() {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName(env.getProperty("jdbc.driverClassName"));
dataSource.setUrl(env.getProperty("jdbc.url"));
dataSource.setUsername(env.getProperty("jdbc.user"));
dataSource.setPassword(env.getProperty("jdbc.pass"));
return dataSource;
}
}
And here's my pom.xml
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<!--Spring dependencies-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-dao</artifactId>
<version>2.0.8</version>
</dependency>
<!-- spring security-->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>${spring-security.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>${spring-security.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>${spring-security.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
<version>${spring-security.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>${aspectj.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectj.version}</version>
</dependency>
<!-- Spring Data JPA dependencies -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>1.6.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>${hibernate.version}</version>
</dependency>
<!--com.mysema.querydsl dependencies-->
<dependency>
<groupId>com.mysema.querydsl</groupId>
<artifactId>querydsl-sql</artifactId>
<version>${com.mysema.querydsl.version}</version>
</dependency>
<dependency>
<groupId>com.mysema.querydsl</groupId>
<artifactId>querydsl-jpa</artifactId>
<version>${com.mysema.querydsl.version}</version>
</dependency>
<dependency>
<groupId>com.mysema.querydsl</groupId>
<artifactId>querydsl-core</artifactId>
<version>${com.mysema.querydsl.version}</version>
</dependency>
<!--Hibernate dependencies-->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>${hibernate.version}</version>
</dependency>
<!--db-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.23</version>
</dependency>
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.4</version>
</dependency>
<!--connection pool-->
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-dbcp</artifactId>
<version>8.0.9</version>
</dependency>
<!--thymeleaf and servlet api-->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring4</artifactId>
<version>2.1.2.RELEASE</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
</build>
It doesn't seem to work for me, i get this error:
Caused by: org.springframework.beans.factory.BeanDefinitionStoreException: Factory method [public org.springframework.transaction.interceptor.BeanFactoryTransactionAttributeSourceAdvisor org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration.transactionAdvisor()] threw exception; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'transactionInterceptor' defined in class path resource [org/springframework/transaction/annotation/ProxyTransactionManagementConfiguration.class]: Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: Property 'transactionManager' is required
any idea why ?
EDIT
it doesn't seem that the entityManagerFactory method is creating properly the bean used in transactionManager, i had the same problem with hibernate (sessionFactory bean is not created, and cannot be used in transactionManager method )
EDIT 2
I got rid of this problem(it was because of a wrong properties file) but now i gey another error :
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [com/servmed/configuration/PersistenceConfig.class]: Invocation of init method failed; nested exception is javax.persistence.PersistenceException: [PersistenceUnit: default] Unable to build Hibernate SessionFactory
Here is how I defined my transaction manager:
#Bean
public Object transactionManager() {
return new org.springframework.orm.jpa.JpaTransactionManager();
}
And instead of calling the entityFactory method, you should perhaps inject it: this would avoid dependency injection errors in entityManagerFactory.
#Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory factory)
{
return new JpaTransactionManager(factory);
}
EDIT
Apart from persistence.xml, the call of afterPropertiesSet() and the setLoadTimeWeaver, we have the same code.
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory()
{
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setGenerateDdl(true);
vendorAdapter.setShowSql(true);
Properties jpaProperties = new Properties();
jpaProperties.put("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto"));
jpaProperties.put("hibernate.dialect" , env.getProperty("hibernate.dialect"));
jpaProperties.put("hibernate.show_sql" , pgadenv.getProperty("hibernate.show_sql"));
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setDataSource(dataSource());
factory.setJpaVendorAdapter(vendorAdapter);
factory.setJpaProperties(jpaProperties);
factory.setPackagesToScan("com.servmed.models");
// factory.afterPropertiesSet(); <-- why ?
// does it work without Weaving ?
factory.setLoadTimeWeaver(new InstrumentationLoadTimeWeaver());
return factory;
}
EDIT (2)
I did not have time to answer you, here is a sample persistence.xml:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1"
xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence
http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
<!-- transaction-type="JTA" -->
<persistence-unit name="persistenceUnit" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<!-- Your probably won't need it. -->
<!-- <class>com.servmed.models.YourClass</class> -->
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode>
<properties>
<property name="javax.persistence.schema-generation.database.action" value="none" />
<property name="javax.persistence.schema-generation.scripts.action" value="none" />
<!-- <property name="javax.persistence.schema-generation.scripts.create-target" value="" /> -->
<!-- <property name="javax.persistence.schema-generation.scripts.drop-target" value=""/> -->
</properties>
</persistence-unit>
</persistence>
You probably won't need to list your classes (like in the commented example), otherwise list them.
And in the Spring configuration, add:
factory.setPersistenceXmlLocation("classpath:META-INF/persistence.xml");
factory.setPersistenceUnitName("persistenceUnit");
However, I'm beginning to think it might be a completely unrelated problem.
#Aissasa-You can use similar JPA Configuration class and you dont have to have any xm file also.
JpaConfiguration
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
import org.eclipse.persistence.config.BatchWriting;
import org.eclipse.persistence.config.PersistenceUnitProperties;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.orm.jpa.JpaBaseConfiguration;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.AbstractJpaVendorAdapter;
import org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
#Configuration
#EnableTransactionManagement
#EnableAutoConfiguration
#EnableJpaRepositories(basePackages = "com.subu")
public class JpaConfiguration extends JpaBaseConfiguration {
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(final EntityManagerFactoryBuilder builder) {
final LocalContainerEntityManagerFactoryBean ret =
builder.dataSource(dataSource())
.packages("com.subu")
.properties(jpaProperties())
.persistenceUnit("com.subu")
.build();
return ret;
}
#Bean
public DataSource dataSource() {
// In classpath from spring-boot-starter-web
final Properties props = new Properties();
props.put("driverClassName", "com.mysql.jdbc.Driver");
props.put("jdbcUrl", "jdbc:mysql://localhost:3306/master?createDatabaseIfNotExist=false");
props.put("username", "root");
props.put("password", "mysql");
HikariConfig hc = new HikariConfig(props);
HikariDataSource ds = new HikariDataSource(hc);
return ds;
}
#Bean
public Map<String, String> jpaProperties() {
Map<String, String> props = new HashMap<>();
props.put("eclipselink.weaving", "false");
props.put("eclipselink.logging.level.sql", "FINE");
props.put("eclipselink.logging.parameters", "true");
props.put("javax.persistence.schema-generation.database.action", "create");
//props.put("javax.persistence.sql-load-script-source", "sql/import.sql");
return props;
}
#Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory emf) {
final MultiTenantJpaTransactionManager transactionManager = new MultiTenantJpaTransactionManager();
transactionManager.setEntityManagerFactory(emf);
return transactionManager;
}
#Override
protected AbstractJpaVendorAdapter createJpaVendorAdapter() {
return new EclipseLinkJpaVendorAdapter();
}
#Override
protected Map<String, Object> getVendorProperties() {
final Map<String, Object> ret = new HashMap<>();
ret.put(PersistenceUnitProperties.BATCH_WRITING, BatchWriting.JDBC);
return ret;
}
}
MultiTenantJpaTransactionManager
import java.io.Serializable;
import javax.persistence.EntityManager;
import org.springframework.orm.jpa.EntityManagerHolder;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.support.TransactionSynchronizationManager;
public class MultiTenantJpaTransactionManager extends JpaTransactionManager {
#Override
protected void doBegin(final Object transaction, final TransactionDefinition definition) {
super.doBegin(transaction, definition);
final EntityManagerHolder emHolder = (EntityManagerHolder) TransactionSynchronizationManager
.getResource(getEntityManagerFactory());
final EntityManager em = emHolder.getEntityManager();
}
}

Resources