/** * */ package com.thinkbiganalytics.jpa; /*- * #%L * thinkbig-commons-jpa * %% * Copyright (C) 2017 ThinkBig Analytics * %% * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * #L% */ import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectReader; import com.fasterxml.jackson.databind.ObjectWriter; import com.fasterxml.jackson.datatype.joda.JodaModule; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import javax.persistence.AttributeConverter; /** * A parameterized converter that converts any object that is serializable to a JSON string stored as a. * single field. The resulting serialized string embeds the java class name of the source object plus its * JSON serialized form as produced by Jackson. Implementations must extend this type in order * to specify the source object type that is converted. This subtype is what is specified * to the @Converter annotation on the field . */ public class JsonAttributeConverter<O> implements AttributeConverter<O, String> { static final Logger LOG = LoggerFactory.getLogger(JsonAttributeConverter.class); private final ObjectWriter writer; private final ObjectReader reader; // private Class<? extends Object> type; public JsonAttributeConverter() { // ResolvableType resType = ResolvableType.forClass(AttributeConverter.class, getClass()); // Class<? extends Object> objType = (Class<? extends Object>) resType.resolveGeneric(0); // this.type = objType; // ObjectMapper mapper = new ObjectMapper(); mapper.registerModule(new JodaModule()); mapper.setSerializationInclusion(Include.NON_NULL); mapper.setVisibility(mapper.getSerializationConfig().getDefaultVisibilityChecker() .withFieldVisibility(JsonAutoDetect.Visibility.ANY) .withGetterVisibility(JsonAutoDetect.Visibility.NONE) .withIsGetterVisibility(JsonAutoDetect.Visibility.NONE)); reader = mapper.reader().forType(JsonWrapper.class); writer = mapper.writer().forType(JsonWrapper.class); } /* (non-Javadoc) * @see javax.persistence.AttributeConverter#convertToDatabaseColumn(java.lang.Object) */ @Override public String convertToDatabaseColumn(O attribute) { try { if (attribute != null) { JsonWrapper<O> wrapper = new JsonWrapper<O>(attribute, writer); String value = writer.writeValueAsString(wrapper); return value; } else { return null; } } catch (JsonProcessingException e) { // TODO Throw a runtime exception? LOG.error("Failed to serialize as object into JSON: {}", attribute, e); return null; } } /* (non-Javadoc) * @see javax.persistence.AttributeConverter#convertToEntityAttribute(java.lang.Object) */ @Override public O convertToEntityAttribute(String dbData) { try { if (dbData != null) { JsonWrapper<O> wrapper = reader.readValue(dbData); O obj = wrapper.readValue(reader); return obj; } else { return null; } } catch (IOException e) { // TODO Throw a runtime exception? LOG.error("Failed to deserialize as object from JSON: {}", dbData, e); return null; } } public static class JsonWrapper<V> { private String type; private String value; public JsonWrapper() { } public JsonWrapper(V obj, ObjectWriter writer) { Class<V> type = (Class<V>) obj.getClass(); this.type = obj.getClass().getName(); try { this.value = writer.forType(type).writeValueAsString(obj); } catch (JsonProcessingException e) { // TODO Throw a runtime exception? JsonAttributeConverter.LOG.error("Failed to serialize as object into JSON: {}", obj, e); } } public V readValue(ObjectReader reader) { try { Class<?> cls = Class.forName(this.type); V obj = reader.forType(cls).readValue(this.value); return obj; } catch (ClassNotFoundException | IOException e) { // TODO Throw a runtime exception? JsonAttributeConverter.LOG.error("Failed to deserialize as object from JSON: {}", this.value, e); return null; } } public String getType() { return type; } public void setType(String type) { this.type = type; } public String getValue() { return value; } public void setValue(String value) { this.value = value; } } }