/**
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you 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.
*/
package org.jasig.portal.dao.usertype;
import java.io.IOException;
import org.jadira.usertype.spi.shared.AbstractStringColumnMapper;
import org.jasig.portal.spring.beans.factory.ObjectMapperFactoryBean;
import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectReader;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
/**
* Mapper that read/writes objects to JSON. Uses a 2 step serialization/deserialization process so the
* object's type can be recorded in the JSON data
*
* @author Eric Dalquist
*/
public class JacksonColumnMapper extends AbstractStringColumnMapper<Object> {
private static final long serialVersionUID = 1L;
private final LoadingCache<Class<?>, ObjectWriter> typedObjectWriters = CacheBuilder.newBuilder()
.build(new CacheLoader<Class<?>, ObjectWriter>() {
@Override
public ObjectWriter load(Class<?> key) throws Exception {
return objectWriter.withType(key);
}
});
private final LoadingCache<Class<?>, ObjectReader> typedObjectReaders = CacheBuilder.newBuilder()
.build(new CacheLoader<Class<?>, ObjectReader>() {
@Override
public ObjectReader load(Class<?> key) throws Exception {
return objectReader.withType(key);
}
});
private final ObjectWriter objectWriter;
private final ObjectReader objectReader;
public JacksonColumnMapper() {
final ObjectMapper mapper;
try {
final ObjectMapperFactoryBean omfb = new ObjectMapperFactoryBean();
omfb.afterPropertiesSet();
mapper = omfb.getObject();
}
catch (Exception e) {
throw new RuntimeException("Failed to create ObjectMapper", e);
}
customizeObjectMapper(mapper);
this.objectWriter = this.createObjectWriter(mapper).withType(JsonWrapper.class);
this.objectReader = this.createObjectReader(mapper).withType(JsonWrapper.class);
}
protected void customizeObjectMapper(ObjectMapper mapper) {
}
protected ObjectWriter createObjectWriter(ObjectMapper mapper) {
return mapper.writer();
}
protected ObjectReader createObjectReader(ObjectMapper mapper) {
return mapper.reader();
}
@Override
public final Object fromNonNullValue(String s) {
try {
final JsonWrapper jsonWrapper = objectReader.readValue(s);
final ObjectReader typeReader = typedObjectReaders.getUnchecked(jsonWrapper.getType());
return typeReader.readValue(jsonWrapper.getValue());
}
catch (JsonProcessingException e) {
throw new IllegalArgumentException("Could not read from JSON: " + s, e);
}
catch (IOException e) {
throw new IllegalArgumentException("Could not read from JSON: " + s, e);
}
}
@Override
public final String toNonNullValue(Object value) {
try {
final Class<? extends Object> type = value.getClass();
final ObjectWriter typeWriter = typedObjectWriters.getUnchecked(type);
final String valueAsString = typeWriter.writeValueAsString(value);
return objectWriter.writeValueAsString(new JsonWrapper(type, valueAsString));
}
catch (JsonGenerationException e) {
throw new IllegalArgumentException("Could not write to JSON: " + value, e);
}
catch (JsonMappingException e) {
throw new IllegalArgumentException("Could not write to JSON: " + value, e);
}
catch (IOException e) {
throw new IllegalArgumentException("Could not write to JSON: " + value, e);
}
}
public static final class JsonWrapper {
private Class<?> t;
private String v;
public JsonWrapper() {
}
public JsonWrapper(Class<?> type, String value) {
this.t = type;
this.v = value;
}
public Class<?> getType() {
return t;
}
public void setType(Class<?> type) {
this.t = type;
}
public String getValue() {
return v;
}
public void setValue(String value) {
this.v = value;
}
@Override
public String toString() {
return "JsonWrapper [type=" + t + ", value=" + v + "]";
}
}
}