package com.fasterxml.jackson.datatype.hibernate5;
import javax.persistence.*;
import java.io.IOException;
import java.util.*;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
import com.fasterxml.jackson.databind.ser.ContainerSerializer;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
import com.fasterxml.jackson.databind.ser.ResolvableSerializer;
import com.fasterxml.jackson.databind.util.NameTransformer;
import com.fasterxml.jackson.datatype.hibernate5.Hibernate5Module.Feature;
import org.hibernate.FlushMode;
import org.hibernate.Hibernate;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.collection.spi.PersistentCollection;
import org.hibernate.engine.spi.PersistenceContext;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.mapping.Bag;
import org.hibernate.resource.transaction.backend.jta.internal.JtaTransactionCoordinatorImpl;
public class PersistentCollectionSerializer
extends ContainerSerializer<Object>
implements ContextualSerializer, ResolvableSerializer
{
private static final long serialVersionUID = 1L;
protected final JavaType _originalType;
protected final int _features;
protected final JsonSerializer<Object> _serializer;
protected final SessionFactory _sessionFactory;
@SuppressWarnings("unchecked")
public PersistentCollectionSerializer(JavaType containerType,
JsonSerializer<?> serializer, int features, SessionFactory sessionFactory) {
super(containerType);
_originalType = containerType;
_serializer = (JsonSerializer<Object>) serializer;
_features = features;
_sessionFactory = sessionFactory;
}
@SuppressWarnings("unchecked")
protected PersistentCollectionSerializer(PersistentCollectionSerializer base, JsonSerializer<?> serializer)
{
super(base);
_originalType = base._originalType;
_serializer = (JsonSerializer<Object>) serializer;
_features = base._features;
_sessionFactory = base._sessionFactory;
}
@Override
public PersistentCollectionSerializer unwrappingSerializer(NameTransformer unwrapper) {
return _withSerializer(_serializer.unwrappingSerializer(unwrapper));
}
protected PersistentCollectionSerializer _withSerializer(JsonSerializer<?> ser) {
if ((ser == _serializer) || (ser == null)) {
return this;
}
return new PersistentCollectionSerializer(this, ser);
}
@Override
protected ContainerSerializer<?> _withValueTypeSerializer(TypeSerializer vts)
{
ContainerSerializer<?> ser0 = _containerSerializer();
if (ser0 != null) {
return _withSerializer(ser0.withValueTypeSerializer(vts));
}
return this;
}
@Override
public void resolve(SerializerProvider provider) throws JsonMappingException
{
if (_serializer instanceof ResolvableSerializer) {
((ResolvableSerializer) _serializer).resolve(provider);
}
}
@Override
public JsonSerializer<?> createContextual(SerializerProvider provider,
BeanProperty property)
throws JsonMappingException
{
JsonSerializer<?> ser = provider.handlePrimaryContextualization(_serializer, property);
if (!usesLazyLoading(property)) {
return ser;
}
return _withSerializer(ser);
}
@Override
public boolean isEmpty(SerializerProvider provider, Object value)
{
if (value == null) {
return true;
}
if (value instanceof PersistentCollection) {
Object lazy = findLazyValue((PersistentCollection) value);
return (lazy == null) || _serializer.isEmpty(provider, lazy);
}
return _serializer.isEmpty(provider, value);
}
@Override
public boolean isUnwrappingSerializer() {
return _serializer.isUnwrappingSerializer();
}
@Override
public boolean usesObjectId() {
return _serializer.usesObjectId();
}
@Override
public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint)
throws JsonMappingException
{
_serializer.acceptJsonFormatVisitor(visitor, typeHint);
}
@Override
public JavaType getContentType() {
ContainerSerializer<?> ser = _containerSerializer();
if (ser != null) {
return ser.getContentType();
}
return _originalType.getContentType();
}
@Override
public JsonSerializer<?> getContentSerializer() {
ContainerSerializer<?> ser = _containerSerializer();
if (ser != null) {
return ser.getContentSerializer();
}
return null;
}
@Override
public boolean hasSingleElement(Object value) {
if (value instanceof Collection<?>) {
return ((Collection<?>) value).size() == 1;
}
if (value instanceof Map<?,?>) {
return ((Map<?,?>) value).size() == 1;
}
return false;
}
@Override
public void serialize(Object value, JsonGenerator g, SerializerProvider provider)
throws IOException
{
if (value instanceof PersistentCollection) {
value = findLazyValue((PersistentCollection) value);
if (value == null) {
provider.defaultSerializeNull(g);
return;
}
}
if (_serializer == null) {
throw JsonMappingException.from(g, "PersistentCollection does not have serializer set");
}
_serializer.serialize(value, g, provider);
}
@Override
public void serializeWithType(Object value, JsonGenerator g, SerializerProvider provider,
TypeSerializer typeSer)
throws IOException
{
if (value instanceof PersistentCollection) {
value = findLazyValue((PersistentCollection) value);
if (value == null) {
provider.defaultSerializeNull(g);
return;
}
}
if (_serializer == null) {
throw JsonMappingException.from(g, "PersistentCollection does not have serializer set");
}
if (Feature.REPLACE_PERSISTENT_COLLECTIONS.enabledIn(_features)) {
value = convertToJavaCollection(value);
}
_serializer.serializeWithType(value, g, provider, typeSer);
}
protected ContainerSerializer<?> _containerSerializer() {
if (_serializer instanceof ContainerSerializer) {
return (ContainerSerializer<?>) _serializer;
}
return null;
}
protected Object findLazyValue(PersistentCollection coll) {
if (!Feature.FORCE_LAZY_LOADING.enabledIn(_features) && !coll.wasInitialized()) {
return null;
}
if (_sessionFactory != null) {
Session session = openTemporarySessionForLoading(coll);
initializeCollection(coll, session);
}
return coll.getValue();
}
private Session openTemporarySessionForLoading(PersistentCollection coll) {
final SessionFactory sf = _sessionFactory;
final Session session = sf.openSession();
PersistenceContext persistenceContext = ((SessionImplementor) session).getPersistenceContext();
persistenceContext.setDefaultReadOnly(true);
session.setFlushMode(FlushMode.MANUAL);
persistenceContext.addUninitializedDetachedCollection(
((SessionFactoryImplementor) _sessionFactory).getCollectionPersister(coll.getRole()),
coll
);
return session;
}
private void initializeCollection(PersistentCollection coll, Session session) {
boolean isJTA = SessionReader.isJTA(session);
if (!isJTA) {
session.beginTransaction();
}
coll.setCurrentSession(((SessionImplementor) session));
Hibernate.initialize(coll);
if (!isJTA) {
session.getTransaction().commit();
}
session.close();
}
protected boolean usesLazyLoading(BeanProperty property) {
if (property != null) {
ElementCollection ec = property.getAnnotation(ElementCollection.class);
if (ec != null) {
return (ec.fetch() == FetchType.LAZY);
}
OneToMany ann1 = property.getAnnotation(OneToMany.class);
if (ann1 != null) {
return (ann1.fetch() == FetchType.LAZY);
}
OneToOne ann2 = property.getAnnotation(OneToOne.class);
if (ann2 != null) {
return (ann2.fetch() == FetchType.LAZY);
}
ManyToOne ann3 = property.getAnnotation(ManyToOne.class);
if (ann3 != null) {
return (ann3.fetch() == FetchType.LAZY);
}
ManyToMany ann4 = property.getAnnotation(ManyToMany.class);
if (ann4 != null) {
return (ann4.fetch() == FetchType.LAZY);
}
return !Feature.REQUIRE_EXPLICIT_LAZY_LOADING_MARKER.enabledIn(_features);
}
return false;
}
private Object convertToJavaCollection(Object value) {
if (!(value instanceof PersistentCollection)) {
return value;
}
if (value instanceof Set) {
return convertToSet((Set<?>) value);
}
if (value instanceof List || value instanceof Bag) {
return convertToList((List<?>) value);
}
if (value instanceof Map) {
return convertToMap((Map<?, ?>) value);
}
throw new IllegalArgumentException("Unsupported PersistentCollection subtype: " + value.getClass());
}
private Object convertToList(List<?> value) {
return new ArrayList<>(value);
}
private Object convertToMap(Map<?, ?> value) {
return new HashMap<>(value);
}
private Object convertToSet(Set<?> value) {
return new HashSet<>(value);
}
protected static class SessionReader
{
public static boolean isJTA(Session session)
{
if (session instanceof EntityManager) {
try {
session.getTransaction();
return false;
} catch (final IllegalStateException e) {
return true;
}
}
if (session instanceof SessionImplementor) {
final Object transactionCoordinator = ((SessionImplementor) session).getTransactionCoordinator();
return (transactionCoordinator instanceof JtaTransactionCoordinatorImpl);
}
return true;
}
}
}