/**
* Licensed to Apereo under one or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information regarding copyright ownership. Apereo
* 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 the
* following location:
*
* <p>http://www.apache.org/licenses/LICENSE-2.0
*
* <p>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.apereo.portal.dao.usertype;
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;
import java.io.IOException;
import org.apereo.portal.spring.beans.factory.ObjectMapperFactoryBean;
import org.jadira.usertype.spi.shared.AbstractStringColumnMapper;
/**
* 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
*
*/
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 + "]";
}
}
}