/* * 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(); } }