package com.kryptnostic.kodex.v1.serialization.jackson; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.lang.reflect.Type; import javax.annotation.Nonnull; import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.InjectableValues.Std; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.dataformat.smile.SmileFactory; import com.fasterxml.jackson.datatype.guava.GuavaModule; import com.fasterxml.jackson.datatype.joda.JodaModule; import com.fasterxml.jackson.module.afterburner.AfterburnerModule; import com.google.common.base.Preconditions; import com.kryptnostic.kodex.v1.crypto.keys.CryptoServiceLoader; import com.kryptnostic.kodex.v1.models.KryptnosticUser; import retrofit.converter.ConversionException; import retrofit.converter.Converter; import retrofit.mime.TypedByteArray; import retrofit.mime.TypedInput; import retrofit.mime.TypedOutput; /** * @author Matthew Tamayo-Rios <matthew@kryptnostic.com> * */ public final class KodexObjectMapperFactory { private final static ObjectMapper globalJsonMapper; private final static ObjectMapper globalSmileMapper; static { globalJsonMapper = newJsonMapper(); configureNoCryptoOnSerialization( globalJsonMapper ); globalSmileMapper = newSmileMapper(); configureNoCryptoOnSerialization( globalSmileMapper ); } private KodexObjectMapperFactory() {}; /** * Returns a global regular json jackson object mapper with {@link JodaModule}, {@link KryptnosticUser}, {@link AfterburnerModule}, and {@link GuavaModule} configured. * * @return */ public static ObjectMapper getObjectMapper() { return globalJsonMapper; } /** * Returns a global binary smile jackson object mapper with {@link JodaModule}, {@link KryptnosticUser}, {@link AfterburnerModule}, and {@link GuavaModule} configured. * decrypt on deserialization. * * @return */ public static ObjectMapper getSmileMapper() { return globalSmileMapper; } public static ObjectMapper getObjectMapper( @Nonnull CryptoServiceLoader loader ) { ObjectMapper mapper = newJsonMapper(); configureCryptoOnSerialization( mapper, Preconditions.checkNotNull( loader, "CryptoServiceLoader cannot be null." ) ); return mapper; } public static ObjectMapper getSmileMapper( @Nonnull CryptoServiceLoader loader ) { ObjectMapper mapper = newSmileMapper(); configureCryptoOnSerialization( mapper, Preconditions.checkNotNull( loader, "CryptoServiceLoader cannot be null." ) ); return mapper; } private static void configureNoCryptoOnSerialization( @Nonnull ObjectMapper mapper ) { Std injectableValues = new Std(); injectableValues.addValue( CryptoServiceLoaderHolder.class, CryptoServiceLoaderHolder.getEmptyHolder() ); mapper.setInjectableValues( injectableValues ); mapper.registerModule( new KryptoModule() ); } private static void configureCryptoOnSerialization( @Nonnull ObjectMapper mapper, @Nonnull CryptoServiceLoader loader ) { Std injectableValues = new Std(); injectableValues.addValue( CryptoServiceLoaderHolder.class, CryptoServiceLoaderHolder.fromLoader( loader ) ); mapper.setInjectableValues( injectableValues ); mapper.registerModule( new KryptoModule( loader ) ); } private static void configureMapper( @Nonnull ObjectMapper mapper ) { mapper.registerModule( new GuavaModule() ); mapper.registerModule( new JodaModule() ); mapper.registerModule( new AfterburnerModule() ); mapper.registerSubtypes( KryptnosticUser.class ); } private static ObjectMapper newJsonMapper() { ObjectMapper mapper = new ObjectMapper(); configureMapper( mapper ); return mapper; } private static ObjectMapper newSmileMapper() { ObjectMapper mapper = new ObjectMapper( new SmileFactory() ); configureMapper( mapper ); return mapper; } // TODO: Get rid of this public static Converter getRetrofitConverter() { return new Converter() { private static final String MIME_TYPE = "application/json; charset=UTF-8"; ObjectMapper objectMapper = getObjectMapper(); @Override public TypedOutput toBody( Object object ) { try { String json = objectMapper.writeValueAsString( object ); return new TypedByteArray( MIME_TYPE, json.getBytes( "UTF-8" ) ); } catch ( JsonProcessingException e ) { throw new AssertionError( e ); } catch ( UnsupportedEncodingException e ) { throw new AssertionError( e ); } } @Override public Object fromBody( TypedInput body, Type type ) throws ConversionException { try { JavaType javaType = objectMapper.getTypeFactory().constructType( type ); InputStream in = body.in(); if ( in.available() == 0 ) { return null; } return objectMapper.readValue( body.in(), javaType ); } catch ( JsonParseException e ) { throw new ConversionException( e ); } catch ( JsonMappingException e ) { throw new ConversionException( e ); } catch ( IOException e ) { throw new ConversionException( e ); } } }; } }