spring mvc

Spring 3 One Approach to Exception Handling for MVC when using JSON

Whenever an exception (such as a binding exception) came up in my Spring 3 MVC app, Spring would return and HTML page with the stack trace. I don’t want this. Besides not exposing the stack trace, I need well formatted JSON in my response, not an HTML page. I haven’t quite got a grip on exception handling in Spring. I’ve read a ton of entries in stack overflow and I get that @Exception works per controller, but for this case, I wanted to handle the exceptions that occur before you even get to the controller. I finally found one model that is working for me. There are probably other ways to do this and I hope to discover and understand them at some point, but this was actually fairly simple.

Thanks to Google App Engine + JAVA + Spring + REST + Mapping Exceptions to Errors for JSON for providing me with a solution. The entry at Wet Feet is very well written. I’m just copying my version here.

1. Create your own class that implements the HandlerExceptionResolver. I used my own view, but the Wet Feet blog entry uses MappingJacksonJsonView. The WebUtil call in the code is my own code to build the JSON.

package com.ahlearns.mystuff;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;

public class JsonExceptionResolver implements HandlerExceptionResolver {
	
	protected final Logger log = LogManager.getLogger(getClass());
	
	@Override
	public ModelAndView resolveException(HttpServletRequest request,
			HttpServletResponse response, Object handler, Exception ex) {
		log.error("Error occurred for handler [" + handler + "]", ex);
		ModelAndView mav = new ModelAndView();
        	mav.setViewName("error");
        	mav.addObject("jsonMsg", WebUtil.getJsonSuccessMsg(false, "An unexpected error occurred."));
        	return mav;
	}
}

2. Add the following bean definition to your spring config. Spring will detect it and route exceptions to the resolver.

<bean id="exceptionResolver" class="com.ahlearns.mystuff.JsonExceptionResolver" />
Advertisements

Spring 3 MVC @RequestBody Deserialization of custom List objects

A few things to make sure Spring and Jackson can correctly deserialize a list of custom objects:

  1. Besides having annotations turned on, you must use @RequestBody in the controller method to indicate you are reading the JSON in the POST request
  2. Make sure your AJAX request has a content-type of “application/json”.  Using Ext JS 4, this is done by sending jsonData instead of params in the Ext.Ajax.request.
  3. Jackson needs the property type to know how deserialize, but Spring doesn’t pass that along in the message converters and might not ever do that,  so you need a wrapper class
  4. If Jackson cannot deserialize your class, then Spring’s MappingJacksonHttpMessageConverter.canRead<?> fails and you get the HttpMediaTypeNotSupportedException (even if the request header has content-type of application/json).  In my case, this was two setters for one property.

I had a few of these things mixed together, so it was a little tricky to unwind, but here’s some code that will deserialize a list of “Users”.  So, that JSON looks something like (w/o the proper quotes):

[{userId: 1, name: John}, {userId: 2, name: Sally}]


// The wrapper class, so that type info (of User) is passed thru the message converters to Jackson
public class Users extends ArrayList<User> {
	private static final long serialVersionUID = 1L;

	public Users() { super(); }
}

// Example method from the controller:
	@RequestMapping(value="/status", method = RequestMethod.POST)
	public @ResponseBody Map<String, ? extends Object> status(@RequestBody Users users) {
		// handle the request here
	}

Helpful links:

The Journey for Handling Hibernate Lazy Loading using Spring 3 MVC Annotations and Jackson for JSON

I’m not even sure where to start with this one. Let’s see…

The Setup

Here’s the big picture.  A new project, new everything, lots of annotations… And I should add, I’m new to Hibernate and Spring…

  • Ext JS 4
  • Spring 3.0.4
  • Hibernate 3.6.2
  • Jackson 1.7.6

In my db, I have a User table.  Each user may have a Supervisor, which is just a user_id.  The User entity ends up looking like this:

@JsonAutoDetect
@Entity
@Table(name="PR_USER")
public class User {

	@Id
	@Column(name="USER_ID")
	private int userId;
	public int getUserId() { return userId; }
	public void setUserId(int userId) { this.userId = userId; }

	@ManyToOne(fetch=FetchType.LAZY)
	@JoinColumn(name="SUPERVISOR_ID")
	private User supervisor;
	public User getSupervisor() { return supervisor; }
	public void setSupervisor(User supervisor) { this.supervisor= supervisor; }
}

Helpful link: How to create relationship to the same entity with JPA (Hibernate)?

Issue 1 – Eagerly Load One Supervisor

Once I got it straight how to create the relationship to the same entity, everything was working well except for one thing… I only needed to eagerly load the user’s supervisor. But I didn’t need the supervisor’s supervisor and their supervisor and so on. After a lot of searching around (and I seem to have lost the link that helped me finally understand, but it might have just been the Hibernate documentation), I determined that in order to only load one “level” of supervisor I needed the “fetch=FetchType.LAZY” property on the @ManyToOne annotation for the supervisor column. Then my query needed to be built with HQL (previously I was using Criterias) so that I could specify to fetch the one supervisor eagerly. The HQL ended up looking something like this:

from User as user
left outer join fetch user.supervisor as supervisor
where user.fname like

I have a few different user queries, by first name, last name, supervisor, etc.

Great! It worked! Now my supervisor chain doesn’t go on and on. Now on to the next issue.

Issue 2 – Jackson mapping of Lazy Loaded objects

All ready to test and then bam! This error from the Jackson mapper:

org.codehaus.jackson.map.JsonMappingException: No serializer found for class org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS) ) (through reference chain: java.util.ArrayList[46]->....User_$$_javassist_2["handler"])
    at org.codehaus.jackson.map.ser.StdSerializerProvider$1.serialize(StdSerializerProvider.java:62)
    at org.codehaus.jackson.map.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:268)
    at org.codehaus.jackson.map.ser.BeanSerializer.serializeFields(BeanSerializer.java:146)
    at org.codehaus.jackson.map.ser.BeanSerializer.serialize(BeanSerializer.java:118)
    at org.codehaus.jackson.map.ser.ContainerSerializers$IndexedListSerializer.serializeContents(ContainerSerializers.java:236)
    at org.codehaus.jackson.map.ser.ContainerSerializers$IndexedListSerializer.serializeContents(ContainerSerializers.java:189)
    at org.codehaus.jackson.map.ser.ContainerSerializers$AsArraySerializer.serialize(ContainerSerializers.java:111)
    at org.codehaus.jackson.map.ser.StdSerializerProvider._serializeValue(StdSerializerProvider.java:296)
    at org.codehaus.jackson.map.ser.StdSerializerProvider.serializeValue(StdSerializerProvider.java:224)
    at org.codehaus.jackson.map.ObjectMapper.writeValue(ObjectMapper.java:925)
    at org.springframework.http.converter.json.MappingJacksonHttpMessageConverter.writeInternal(MappingJacksonHttpMessageConverter.java:153)

So, it turns out that Jackson does not know what to do with a Hibernate Lazy Loaded object. A lot of research and these helpful links led me to the following conclusion:

  1. There is a new, very new, completed untested Jackson-hibernate module out there to handle lazy loading, named jackson-module-hibernate.  https://github.com/FasterXML/jackson-module-hibernate.
  2. The example setup in jackson-module-hibernate has a configuration for AnnotationMethodHandlerAdapter.  Close reading of the comments in the “Serializing Hibernate entities into JSON using Jackson framwork” extremely helpful blog entry, says the AnnotationMethodHandlerAdpater configs are for REST only.  Currently, my setup is to use @ResponseBody to write the JSON the response, which uses AnnotationMethodHandlerAdapter.  So, it seems there are now two options.
    • No longer use @ResponseBody and instead create a “jsonView” and return that as the ModelAndView, as suggested in the comments of the post.
    • Make AnnotationMethodHandlerAdapter work.  This lead me to the incredibly helpful post: http://scottfrederick.blogspot.com/2011/03/customizing-spring-3-mvcannotation.html.  Which clearly explains the problem with AnnotationMethodHandlerAdapter and mvc annotations and a workaround for it, unless you can hang around for Spring 3.1.

I should also mention that I had to make changes to jackson-module-hibernate.  The key piece of code was commented out and the parameters were wrong.  So, after changing


public class HibernateSerializers implements Serializers
{
    ....
   public JsonSerializer<?> findSerializer(
        SerializationConfig config, JavaType type,
        BeanDescription beanDesc, BeanProperty beanProperty )
   {
       ....
       if (HibernateProxy.class.isAssignableFrom(raw)) {
        //  return new HibernateProxySerializer(type, config);
        //  return new PersistentCollectionSerializer(type, isEnabled(Feature.FORCE_LAZY_LOADING));
       }
      ....
   }
}

to

public class HibernateSerializers implements Serializers
{
    ....
    public JsonSerializer<?> findSerializer(
         SerializationConfig config, JavaType type,
         BeanDescription beanDesc, BeanProperty beanProperty )
    {
        ....
        if (HibernateProxy.class.isAssignableFrom(raw)) {
            return new HibernateProxySerializer( isEnabled(Feature.FORCE_LAZY_LOADING));
         // return new PersistentCollectionSerializer(type, isEnabled(Feature.FORCE_LAZY_LOADING));
        }
        ....
     }
}

With the above change to jackson-module-hibernate and also implementing Scott Frederick’s AnnotationMethodHandlerAdapterConfigurer, amazingly, it worked.  Hibernate only loaded the supervisor one level deep and Jackson handled the lazy loaded object and mapped the JSON as I expected and Ext JS showed the user data in my grid.

It was a journey…