Skip to content

Codec System

DuyHai DOAN edited this page Sep 6, 2016 · 4 revisions

Definition and usage

Sometimes it is useful to be able to transform a Java type into a native Java type supported by Cassandra. An example could be to convert Joda DateTime into java.util.Date.

To support such usage, Achilles lets you define your own transformation using @Codec on each field of your entity.

The definition of the @Codec annotation is:

    public @interface Codec {
    
        /**
         * Codec Implementation class. The provided Codec class should implement the {@link info.archinnov.achilles.type.codec.Codec} interface.
         */
        Class<? extends info.archinnov.achilles.type.codec.Codec> value();
    }
        

To define a type transformer, you need to provide a codec class. Your codec class should implement the Codec interface.

Example:


Let's consider the following codec transforming a Long to a String
    public class LongToString implements Codec<Long,String> {
        @Override
        public Class<Long> sourceType() {
            return Long.class;
        }
    
        @Override
        public Class<String> targetType() {
           return String.class;
        }
    
        @Override
        public String encode(Long fromJava) throws AchillesTranscodingException {
           return fromJava.toString();
        }
    
        @Override
        public Long decode(String fromCassandra) throws AchillesTranscodingException {
           return Long.parseLong(fromCassandra);
        }
    }

Example of simple Long type to String type transformation
    @Column
    @Codec(LongToString.class)
    private Long longToString;
    

Example of List<Long> to List<String> transformation
    @Column
    private List<@Codec(LongToString.class) Long> listOfLong;

Example of Set<Long> to Set<String> transformation
    
    @Column
    private Set<@Codec(LongToString.class) Long> setOfLong;

Example of key Map transformation: Map<Long,Double> to Map<String,Double>
    @Column
    private Map<@Codec(LongToString.class) Long, Double> mapKeyTransformation;

Example of value Map transformation: Map<Integer,Long> to Map<Integer,String>
    @Column
    private Map<Integer,@Codec(LongToString.class) Long> mapValueTransformation;

You can also set the @Codec annotation on the target class. Example:

 
    // Codec definition
    public class BeanToStringCodec implements Codec<MyBean, String> {
    
        @Override
        public Class<MyBean> sourceType() { return MyBean.class };

        @Override
        public Class<String> targetType() { return String.class };

        @Override
        public String encode(MyBean fromJava) throws AchillesTranscodingException {...};

        @Override
        public MyBean decode(String fromCassandra) throws AchillesTranscodingException {...};    
    }
    
    
    // Custom bean with @Codec annotation
    @Codec(BeanToStringCodec.class)
    public class MyBean {
    
        ...
    }    
 
    //Usage
    
    @Table
    public class MyEntity {
    
        ...
        
        @Column
        private MyBean myBean; //No need to add @Codec because already defined on class MyBean
    }

Codec declaration and type

There are 2 ways to declare your codec:

  • directly on-site using the @Codec annotation put on the field, as shown above
  • inside a class, abstract class or interface annotated by @CodecRegistry

If a codec is both declared in a @CodecRegistry and on-site. The on-site definition will have higher priority. Example:

    @CodecRegistry
    public interface MyCodecRegistry {
        
        @Enumerated(Encoding.NAME)
        MyEnum myEnum;
    }
    
    @Table
    public class MyEntity {
    
        @Enumerated(Encoding.ORDINAL) // has higher priority than @Enumerated(Encoding.NAME) in MyCodecRegistry
        @Column
        private MyEnum myEnum;
    }

There are 2 types of codec:

  • simple codecs using the @Codec as shown above
  • runtime codecs which are instantiated and injected into Achilles at runtime

Codec Registry

Marker annotation to be used on configuration class for compile-time code generation. The type (class, abstract class or interface) having this annotation will expose a list of codecs to be used by Achilles during source code parsing.

Ex:

    @CodecRegistry
    public [class | abstract class | interface] MyCodecRegistry {
    
        //Source type = int, target type = String (according to IntToStringCodec codec)
        @Codec(IntToStringCodec.class)
        private int intToString;
    
        //Source type = MyOwnType, target type = String (according to MyOwnTypeToStringCodec codec)
        @Codec(MyOwnTypeToStringCodec.class)
        private MyOwnType myOwnTypeToString;
    
        //Source type = AnotherBean, target type = String (because of {@literal @}JSON)
        @JSON
        private AnotherBean beanToJSON;
    
        //Source type = MyEnum, target type = int (because of Encoding.ORDINAL)
        @Enumerated(Encoding.ORDINAL)
        private MyEnum enumToOrdinal;
    }    

It is possible to declare several codec registries in your source code, just annotate them with @CodecRegistry

Warning: it is not possible to declare 2 different codecs for the same source type for all registered codec registries. Achilles will raise a compilation error when encountering such case

Ex:

    @CodecRegistry
    public class MyCodecRegistry {
    
         @Codec(MyOwnTypeToStringCodec.class)
         private MyOwnType myOwnTypeToString;
    
         // ERROR, not possible to have a 2nd codec for the same source type MyOwnType
         @Codec(MyOwnTypeToBytesCodec.class)
         private MyOwnType myOwnTypeToBytes;
    }

Runtime Codec

Transform a custom Java type into one of native types supported by the Java driver. Normally you'll use the @Codec annotation and provide a codec class but if your codec class is stateful or its construction needs some external dependencies and cannot be instantiated using the default no-args constructor, you can register the codec using this annotation and build it at runtime before injecting it into Achilles

Ex:

   //Compile time
   @Column
   @RuntimeCodec(cqlClass = String.class)
   private MyBean bean;
  
   //Runtime
   final Cluster cluster = .... // Create Java driver cluster object
   final Codec<MyBean, String> statefulCodec = new .... // Create your codec with initialization logic here
   final CodecSignature<MyBean, String> codecSignature = new CodecSignature(MyBean.class, String.class);
  
   ManagerFactory factory = ManagerFactoryBuilder
                                .builder(cluster)
                                ...
                                .withRuntimeCodec(codecSignature, codec)
                                .build();

A codec is looked up and identified uniquely at runtime using the following information:

  • sourceType
  • targetType (see cqlClass below)
  • optionally, codecName (see below) if provided

The @RuntimeCodec annotation has 2 attributes:

  1. cqlClass (MANDATORY): specify the target CQL type for the runtime codec. It is necessary to provide this type at compile time so that Achilles can generate appropriate meta data
  2. codecName (OPTIONAL): useful to distinguish 2 different codecs having the same sourceType and targetType

Home

Clone this wiki locally