/*
* This is eMonocot, a global online biodiversity information resource.
*
* Copyright © 2011–2015 The Board of Trustees of the Royal Botanic Gardens, Kew and The University of Oxford
*
* eMonocot is free software: you can redistribute it and/or modify it under the terms of the
* GNU Affero General Public License as published by the Free Software Foundation, either version 3
* of the License, or (at your option) any later version.
*
* eMonocot is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* The complete text of the GNU Affero General Public License is in the source repository as the file
* ‘COPYING’. It is also available from <http://www.gnu.org/licenses/>.
*/
package org.emonocot.model.marshall.json.hibernate;
import java.io.IOException;
import org.hibernate.proxy.HibernateProxy;
import org.hibernate.proxy.LazyInitializer;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
import com.fasterxml.jackson.databind.ser.impl.PropertySerializerMap;
/**
* Serializer to use for values proxied using {@link HibernateProxy}.
*<p>
* TODO: should try to make this work more like Jackson
* <code>BeanPropertyWriter</code>, possibly sub-classing
* it -- it handles much of functionality we need, and has
* access to more information than value serializers (like
* this one) have.
*/
public class HibernateProxySerializer
extends JsonSerializer<HibernateProxy>
{
/**
* Property that has proxy value to handle
*/
protected final BeanProperty _property;
protected final boolean _forceLazyLoading;
/**
* For efficient serializer lookup, let's use this; most
* of the time, there's just one type and one serializer.
*/
protected PropertySerializerMap _dynamicSerializers;
/*
/**********************************************************************
/* Life cycle
/**********************************************************************
*/
public HibernateProxySerializer(boolean forceLazyLoading)
{
_property = null;
_forceLazyLoading = forceLazyLoading;
_dynamicSerializers = PropertySerializerMap.emptyMap();
}
/*
/**********************************************************************
/* JsonSerializer impl
/**********************************************************************
*/
@Override
public void serialize(HibernateProxy value, JsonGenerator jgen, SerializerProvider provider)
throws IOException, JsonProcessingException
{
Object proxiedValue = findProxied(value);
// TODO: figure out how to suppress nulls, if necessary? (too late for that here)
if (proxiedValue == null) {
provider.defaultSerializeNull(jgen);
return;
}
findSerializer(provider, proxiedValue).serialize(proxiedValue, jgen, provider);
}
public void serializeWithType(HibernateProxy value, JsonGenerator jgen, SerializerProvider provider,
TypeSerializer typeSer)
throws IOException, JsonProcessingException
{
Object proxiedValue = findProxied(value);
if (proxiedValue == null) {
provider.defaultSerializeNull(jgen);
return;
}
/* This isn't exactly right, since type serializer really refers to proxy
* object, not value. And we really don't either know static type (necessary
* to know how to apply additional type info) or other things;
* so it's not going to work well. But... we'll do out best.
*/
findSerializer(provider, proxiedValue).serializeWithType(proxiedValue, jgen, provider, typeSer);
}
/*
/**********************************************************************
/* Helper methods
/**********************************************************************
*/
protected JsonSerializer<Object> findSerializer(SerializerProvider provider, Object value)
throws IOException, JsonProcessingException
{
/* TODO: if Hibernate did use generics, or we wanted to allow use of Jackson
* annotations to indicate type, should take that into account.
*/
Class<?> type = value.getClass();
/* we will use a map to contain serializers found so far, keyed by type:
* this avoids potentially costly lookup from global caches and/or construction
* of new serializers
*/
PropertySerializerMap.SerializerAndMapResult result = _dynamicSerializers.findAndAddSerializer(type,
provider, _property);
if (_dynamicSerializers != result.map) {
_dynamicSerializers = result.map;
}
return result.serializer;
}
/**
* Helper method for finding value being proxied, if it is available
* or if it is to be forced to be loaded.
*/
protected Object findProxied(HibernateProxy proxy)
{
LazyInitializer init = proxy.getHibernateLazyInitializer();
if (!_forceLazyLoading && init.isUninitialized()) {
return null;
}
return init.getImplementation();
}
}