Issue
I am getting the following error response from the server.
HTTP Status 500 -
type Exception report
message
descriptionThe server encountered an internal error () that prevented it from fulfilling this request.
exception
javax.servlet.ServletException: java.lang.UnsupportedOperationException: Attempted to serialize java.lang.Class: org.hibernate.proxy.HibernateProxy. Forgot to register a type adapter?
root cause
java.lang.UnsupportedOperationException: Attempted to serialize java.lang.Class: org.hibernate.proxy.HibernateProxy. Forgot to register a type adapter?
From Java debugger:
org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer@7632012e
I am using Gson to convert my Java objects to JSON. Below I have pasted some of my code.
This is my resource:
@Stateless
@LocalBean
@Path("/autos")
@Produces(MediaType.APPLICATION_JSON)
public class AutoResource {
@EJB
private CarAssembler warehouse;
@Context
private UriInfo uriInfo;
@GET
public Response allAutos() {
// Building a context, lots of code...
// Creating a Gson instance and configures it...
final Auto auto = warehouse.list(context);
final String autoJson = gson.toJson(auto);
return Response.ok(autoJson).build();
}
}
The CarAssembler is just an service which invokes a repository. I have not pasted the code of the service here.
Repository:
@Override
public Question findById(final int id, final FetchType fetchType) {
final Auto question = getEntityManager().find(Auto.class, id);
if (fetchType == FetchType.LAZY) {
return auto;
}
Hibernate.initialize(auto.getManufacturer());
Hibernate.initialize(auto.getAssemblyHouse());
return auto;
}
As you see, I provide both lazy and eager loading of objects. I use Hibernate.initialize to eager fetch JPA associations. However, the question is how I can fix the proxy error I am getting. Why come there is only AssemblyHouse that is still attached to JavaAssist, while Manufacturer is not (I have seen the type in Java Debugger). How do I know when to unproxy objects? Should I unproxy all associations this auto may have? And in which layer of my code? Does it affect the performance of my application when I unproxy? Are there other solutions? I see from the error message that I can make a type adapter. Yes, I could but then I have to do that for all domain objects to be sure that conversion is done correctly. Maybe other objects in my domain starts failing when I try to convert it into JSON representation too, but I don't know when or why. Is it just luck that the other objects are fine?
This is the way I am unproxing objects, but I have not implemented it yet, because I don't know if this is good or bad, and in what layer to do this, and when I should do it. Should I unproxy objects all the time?
public class HibernateUtilities {
public static <T> T unproxy(T proxy) {
if (proxy == null) {
return null;
}
if (proxy instanceof HibernateProxy) {
Hibernate.initialize(proxy);
HibernateProxy hibernateProxy = (HibernateProxy) proxy;
T unproxiedObject = (T) hibernateProxy.getHibernateLazyInitializer().getImplementation();
return unproxiedObject;
}
return proxy;
}
}
Stacktrace as requested:
[#|2012-11-22T17:17:13.285+0100|WARNING|glassfish3.1.2|javax.enterprise.system.container.web.com.sun.enterprise.web|_ThreadID=71;_ThreadName=Thread-8;|StandardWrapperValve[javax.ws.rs.core.Application]: PWC1406: Servlet.service() for servlet javax.ws.rs.core.Application threw exception java.lang.UnsupportedOperationException: Attempted to serialize java.lang.Class: org.hibernate.proxy.HibernateProxy. Forgot to register a type adapter? at com.google.gson.internal.bind.TypeAdapters$1.write(TypeAdapters.java:64) at com.google.gson.internal.bind.TypeAdapters$1.write(TypeAdapters.java:61) at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:68) at com.google.gson.internal.bind.ArrayTypeAdapter.write(ArrayTypeAdapter.java:93) at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:68) at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.write(ReflectiveTypeAdapterFactory.java:89) at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:195) at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:68) at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.write(ReflectiveTypeAdapterFactory.java:89) at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:195) at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:68) at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.write(ReflectiveTypeAdapterFactory.java:89) at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:195) at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:68) at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.write(CollectionTypeAdapterFactory.java:96) at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.write(CollectionTypeAdapterFactory.java:60) at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:68) at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.write(ReflectiveTypeAdapterFactory.java:89) at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:195) at com.google.gson.Gson.toJson(Gson.java:586) at com.google.gson.Gson.toJson(Gson.java:565) at com.google.gson.Gson.toJson(Gson.java:520) at com.myapp.AutoResource.produceAuto(AutoResource.java:48) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:601) at org.glassfish.ejb.security.application.EJBSecurityManager.runMethod(EJBSecurityManager.java:1052) at org.glassfish.ejb.security.application.EJBSecurityManager.invoke(EJBSecurityManager.java:1124) at com.sun.ejb.containers.BaseContainer.invokeBeanMethod(BaseContainer.java:5388) at com.sun.ejb.EjbInvocation.invokeBeanMethod(EjbInvocation.java:619) at com.sun.ejb.containers.interceptors.AroundInvokeChainImpl.invokeNext(InterceptorManager.java:800) at com.sun.ejb.EjbInvocation.proceed(EjbInvocation.java:571) at com.sun.ejb.containers.interceptors.SystemInterceptorProxy.doAround(SystemInterceptorProxy.java:162) at com.sun.ejb.containers.interceptors.SystemInterceptorProxy.aroundInvoke(SystemInterceptorProxy.java:144) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:601) at com.sun.ejb.containers.interceptors.AroundInvokeInterceptor.intercept(InterceptorManager.java:861) at com.sun.ejb.containers.interceptors.AroundInvokeChainImpl.invokeNext(InterceptorManager.java:800) at com.sun.ejb.containers.interceptors.InterceptorManager.intercept(InterceptorManager.java:370) at com.sun.ejb.containers.BaseContainer.__intercept(BaseContainer.java:5360) at com.sun.ejb.containers.BaseContainer.intercept(BaseContainer.java:5348) at com.sun.ejb.containers.EJBLocalObjectInvocationHandler.invoke(EJBLocalObjectInvocationHandler.java:214) at com.sun.ejb.containers.EJBLocalObjectInvocationHandlerDelegate.invoke(EJBLocalObjectInvocationHandlerDelegate.java:89) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:601) at com.sun.jersey.spi.container.JavaMethodInvokerFactory$1.invoke(JavaMethodInvokerFactory.java:60) at com.sun.jersey.server.impl.model.method.dispatch.AbstractResourceMethodDispatchProvider$ResponseOutInvoker._dispatch(AbstractResourceMethodDispatchProvider.java:205) at com.sun.jersey.server.impl.model.method.dispatch.ResourceJavaMethodDispatcher.dispatch(ResourceJavaMethodDispatcher.java:75) at com.sun.jersey.server.impl.uri.rules.HttpMethodRule.accept(HttpMethodRule.java:288) at com.sun.jersey.server.impl.uri.rules.ResourceClassRule.accept(ResourceClassRule.java:108) at com.sun.jersey.server.impl.uri.rules.RightHandPathRule.accept(RightHandPathRule.java:147) at com.sun.jersey.server.impl.uri.rules.RootResourceClassesRule.accept(RootResourceClassesRule.java:84) at com.sun.jersey.server.impl.application.WebApplicationImpl._handleRequest(WebApplicationImpl.java:1469) at com.sun.jersey.server.impl.application.WebApplicationImpl._handleRequest(WebApplicationImpl.java:1400) at com.sun.jersey.server.impl.application.WebApplicationImpl.handleRequest(WebApplicationImpl.java:1349) at com.sun.jersey.server.impl.application.WebApplicationImpl.handleRequest(WebApplicationImpl.java:1339) at com.sun.jersey.spi.container.servlet.WebComponent.service(WebComponent.java:416) at com.sun.jersey.spi.container.servlet.ServletContainer.service(ServletContainer.java:537) at com.sun.jersey.spi.container.servlet.ServletContainer.service(ServletContainer.java:708) at javax.servlet.http.HttpServlet.service(HttpServlet.java:770) at org.apache.catalina.core.StandardWrapper.service(StandardWrapper.java:1550) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:281) at org.apache.catalina.core.StandardContextValve.__invoke(StandardContextValve.java:175) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java) at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:655) at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:595) at org.apache.catalina.core.StandardHostValve.__invoke(StandardHostValve.java:161) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java) at org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:331) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:231) at com.sun.enterprise.v3.services.impl.ContainerMapper$AdapterCallable.call(ContainerMapper.java:317) at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:195) at com.sun.grizzly.http.ProcessorTask.invokeAdapter(ProcessorTask.java:860) at com.sun.grizzly.http.ProcessorTask.doProcess(ProcessorTask.java:757) at com.sun.grizzly.http.ProcessorTask.process(ProcessorTask.java:1056) at com.sun.grizzly.http.DefaultProtocolFilter.execute(DefaultProtocolFilter.java:229) at com.sun.grizzly.DefaultProtocolChain.executeProtocolFilter(DefaultProtocolChain.java:137) at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:104) at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:90) at com.sun.grizzly.http.HttpProtocolChain.execute(HttpProtocolChain.java:79) at com.sun.grizzly.ProtocolChainContextTask.doCall(ProtocolChainContextTask.java:54) at com.sun.grizzly.SelectionKeyContextTask.call(SelectionKeyContextTask.java:59) at com.sun.grizzly.ContextTask.run(ContextTask.java:71) at com.sun.grizzly.util.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:532) at com.sun.grizzly.util.AbstractThreadPool$Worker.run(AbstractThreadPool.java:513) at java.lang.Thread.run(Thread.java:722) |#]
Solution
You can do without manually unproxying everything by using a custom TypeAdapter
.
Something along these lines:
/**
* This TypeAdapter unproxies Hibernate proxied objects, and serializes them
* through the registered (or default) TypeAdapter of the base class.
*/
public class HibernateProxyTypeAdapter extends TypeAdapter<HibernateProxy> {
public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() {
@Override
@SuppressWarnings("unchecked")
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
return (HibernateProxy.class.isAssignableFrom(type.getRawType()) ? (TypeAdapter<T>) new HibernateProxyTypeAdapter(gson) : null);
}
};
private final Gson context;
private HibernateProxyTypeAdapter(Gson context) {
this.context = context;
}
@Override
public HibernateProxy read(JsonReader in) throws IOException {
throw new UnsupportedOperationException("Not supported");
}
@SuppressWarnings({"rawtypes", "unchecked"})
@Override
public void write(JsonWriter out, HibernateProxy value) throws IOException {
if (value == null) {
out.nullValue();
return;
}
// Retrieve the original (not proxy) class
Class<?> baseType = Hibernate.getClass(value);
// Get the TypeAdapter of the original class, to delegate the serialization
TypeAdapter delegate = context.getAdapter(TypeToken.get(baseType));
// Get a filled instance of the original class
Object unproxiedValue = ((HibernateProxy) value).getHibernateLazyInitializer()
.getImplementation();
// Serialize the value
delegate.write(out, unproxiedValue);
}
}
To use it you must first register it:
GsonBuilder b = new GsonBuilder();
...
b.registerTypeAdapterFactory(HibernateProxyTypeAdapter.FACTORY);
...
Gson gson = b.create();
Notice that this will recursively initialize every proxy you have in the object hierarchy; since however you have to serialize the whole data, you should have done that anyway.
How does this work?
GSON contains a number of TypeAdapterFactory
implementations, for various types (primitive types, common types like String
or Date
, lists, arrays...). Each factory is asked if it is able to serialize a certain Java type (the parameter to create
is a TypeToken
instead of a Class
in order to capture possible information about generic types, which Class
does not have). If the factory is able to serialize/deserialize a type, it responds with a TypeAdapter
instance; otherwise it responds with null
.
HibernateProxyTypeAdapter.FACTORY
verifies whether type implements HibernateProxy
; in that case, it returns an instance of HibernateProxyTypeAdapter
for serialization.
The write
method is called when an actual object has to be serialized; the adapter extracts the original type of the underlying object, and asks GSON for the standard TypeAdapter
for the original type, which generally is a ReflectiveTypeAdapter
.
Then it retrieves an instance of the original class, instead of directly using the proxy. This is necessary because ReflectiveTypeAdapter
accesses directly to fields, instead of using getters; accessing to the fields of a proxied object does not work, and is a classical Hibernate pitfall.
As a possible performance improvement, the delegate TypeAdapter
should be acquired in the create
method. I found out that calling getSuperclass()
on the proxy Class
appears to yield the original base class. The code can then become:
public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() {
@Override
@SuppressWarnings("unchecked")
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
return (HibernateProxy.class.isAssignableFrom(type.getRawType())
? (TypeAdapter<T>) new HibernateProxyTypeAdapter((TypeAdapter)gson.getAdapter(TypeToken.get(type.getRawType().getSuperclass())))
: null);
}
};
private final TypeAdapter<Object> delegate;
private HibernateProxyTypeAdapter(TypeAdapter<Object> delegate) {
this.delegate = delegate;
}
@SuppressWarnings({"rawtypes", "unchecked"})
@Override
public void write(JsonWriter out, HibernateProxy value) throws IOException {
if (value == null) {
out.nullValue();
return;
}
delegate.write(out, ((HibernateProxy) value).getHibernateLazyInitializer()
.getImplementation());
}
Answered By - Flavio
Answer Checked By - David Marino (JavaFixing Volunteer)