Quick Post: Integrating Spring and Jersey
02/02/2011
2 comments
When: When you need Spring to manage Jersey´s libraries, but still want to be free to develop your services using JAX-RS.
When not: If your team does not know REST, maybe it´s not a good idea to use it in development, because REST has its own culture – Uniform Interfaces, Hypermedia, the concept of Resource-Oriented, etc.
How:
BaseEntity.java
package com.alesaudate.domain;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
import javax.persistence.Version;
import org.hibernate.validator.ClassValidator;
import org.hibernate.validator.InvalidValue;
@MappedSuperclass
public abstract class BaseEntity {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
@Version
private Long version;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Long getVersion() {
return version;
}
public void setVersion(Long version) {
this.version = version;
}
/**
Validation code, that is valid for all subclasses.
*/
public void validate () throws ValidationException {
ClassValidator validator = new ClassValidator(getClass());
InvalidValue[] invalidValues = validator.getInvalidValues(this);
if (invalidValues != null && invalidValues.length > 0) {
throw new ValidationException(buildValidationExceptionMessage(invalidValues));
}
}
public String buildValidationExceptionMessage (InvalidValue[] invalidValues) {
StringBuilder builder = new StringBuilder();
for (InvalidValue value : invalidValues) {
builder.append(value.toString()).append("\n");
}
return builder.toString();
}
}
BaseService.java
package com.alesaudate.services;
import java.util.ArrayList;
import java.util.List;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Restrictions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.hibernate3.HibernateTemplate;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import com.alesaudate.domain.BaseEntity;
import com.alesaudate.domain.InvalidStateException;
import com.alesaudate.domain.Person;
import com.alesaudate.services.support.collections.Collection;
@Component
public abstract class BaseService<T extends BaseEntity> {
@Autowired
private HibernateTemplate hibernateTemplate;
@Transactional
public T createOnDatabase (T entity) throws InvalidStateException {
entity.validate();
getHibernateTemplate().persist(entity);
return entity;
}
@POST
@Produces({MediaType.TEXT_XML, MediaType.APPLICATION_XML})
public T create (T entity) throws InvalidStateException {
return createOnDatabase(entity);
}
@Transactional
public T updateOnDatabase (T entity) throws InvalidStateException {
entity.validate();
getHibernateTemplate().update(entity);
return entity;
}
@PUT
@Produces({MediaType.TEXT_XML, MediaType.APPLICATION_XML})
public T update (T entity) throws InvalidStateException {
return updateOnDatabase(entity);
}
@Transactional(readOnly=true)
public T findOnDatabase (Long id) {
DetachedCriteria criteria = DetachedCriteria.forClass(getManagedClass()).add(Restrictions.eq("id", id));
List entities = getHibernateTemplate().findByCriteria(criteria);
if (entities.isEmpty())
return null;
return (T)entities.get(0);
}
@GET
@Path("{id}")
@Produces({MediaType.TEXT_XML, MediaType.APPLICATION_XML})
public T find (@PathParam("id")Long id) {
T entity = findOnDatabase(id);
loadEntity(entity);
return entity;
}
@Transactional(readOnly=true)
public List<T > findAllFromDatabase () {
DetachedCriteria criteria = DetachedCriteria.forClass(getManagedClass());
List<T> all = getHibernateTemplate().findByCriteria(criteria);
loadList(all);
return all;
}
@GET
@Produces({MediaType.TEXT_XML, MediaType.APPLICATION_XML})
@Collection
public List<T> findAll() {
return findAllFromDatabase();
}
@Transactional
public void deleteFromDatabase (T toDelete) {
getHibernateTemplate().delete(toDelete);
}
@DELETE
@Produces({MediaType.TEXT_XML, MediaType.APPLICATION_XML})
public void delete (T toDelete) {
deleteFromDatabase(toDelete);
}
public abstract Class<? extends BaseEntity> getManagedClass();
public abstract void loadEntity (T data);
public abstract void loadList (java.util.Collection<T> data);
public void setHibernateTemplate(HibernateTemplate hibernateTemplate) {
this.hibernateTemplate = hibernateTemplate;
}
public HibernateTemplate getHibernateTemplate() {
return hibernateTemplate;
}
}
PersonService.java
package com.alesaudate.services;
import java.util.Collection;
import javax.ws.rs.Path;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Restrictions;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import com.alesaudate.domain.Address;
import com.alesaudate.domain.BaseEntity;
import com.alesaudate.domain.Person;
@Component
@Path("/person")
public class PersonService extends BaseService<Person>{
@Override
public Class<? extends BaseEntity> getManagedClass() {
return Person.class;
}
@Override
public void loadEntity(Person data) {
//Load addresses
data.getAddresses();
}
@Override
@Transactional(propagation=Propagation.MANDATORY)
public void loadList(Collection<Person> data) {
for (Person p : data) {
getHibernateTemplate().find("select p.addresses from Person p");
for (Address address : p.getAddresses()) {
getHibernateTemplate().evict(address);
address.makeXMLCompatible();
}
getHibernateTemplate().evict(p);
}
}
}
applicationContext.xml:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <context:component-scan base-package="com.alesaudate.services" /> <!-- Other bean definitions... --> </beans>
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
<display-name>Architecture</display-name>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>
<servlet>
<servlet-name>Jersey Servlet</servlet-name>
<servlet-class>com.sun.jersey.spi.spring.container.servlet.SpringServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Jersey Servlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
End.
Categories: Architecture
JAX-RS, quick post, REST, Spring, Tutorial