Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Polymorphism and inheritance in Avro schemas

Tags:

java

avro

Is it possible to write an Avro schema/IDL that will generate a Java class that either extends a base class or implements an interface? It seems like the generated Java class extends the org.apache.avro.specific.SpecificRecordBase. So, the implements might be the way to go. But, I don't know if this is possible.

I have seen examples with suggestions to define an explicit "type" field in each specific schema, with more of an association than inheritance semantics.

I use my base class heavily in my factory classes and other parts of the code with generics like <T extends BaseObject>. Currently, I had it code generated from the JSON Schema, which supports inheritance.

Another side question: can you use IDL to define just records without the protocol definition? I think the answer is no because the compiler complains about the missing protocol keyword.

Help appreciated! Thanks.

like image 380
bsam Avatar asked Jan 01 '14 00:01

bsam


People also ask

Does Avro schema support inheritance?

Avro doesn't support inheritance between records, so any OOP strategy to have assets inherit properties from a common ancestor is unfortunately not viable.

What are Avro schemas?

Avro schema definitions are JSON records. Because it is a record, it can define multiple fields which are organized in a JSON array. Each such field identifies the field's name as well as its type. The type can be something simple, like an integer, or something complex, like another record.

What is schema evolution in Avro?

Schema evolution is the term used for how the store behaves when Avro schema is changed after data has been written to the store using an older version of that schema.


2 Answers

I found a better way to solve this problem. Looking at the Schema generation source in Avro, I figured out that internally the class generation logic uses Velocity schemas to generate the classes.

I modified the record.vm template to also implement my specific interface. There is a way to specify the location of velocity directory using the templateDirectory configuration in the maven build plugin.

I also switched to using SpecificDatumWriter instead of reflectDatumWriter.

<plugin>   <groupId>org.apache.avro</groupId>   <artifactId>avro-maven-plugin</artifactId>    <version>${avro.version}</version>    <executions>     <execution>       <phase>generate-sources</phase>       <goals>         <goal>schema</goal>       </goals>       <configuration>          <sourceDirectory>${basedir}/src/main/resources/avro/schema</sourceDirectory>          <outputDirectory>${basedir}/target/java-gen</outputDirectory>          <fieldVisibility>private</fieldVisibility>          <stringType>String</stringType>          <templateDirectory>${basedir}/src/main/resources/avro/velocity-templates/</templateDirectory>        </configuration>     </execution>   </executions> </plugin> 
like image 101
bsam Avatar answered Sep 22 '22 17:09

bsam


I hope it will be helpful for others if I'll write it here that I've created maven plugin for exactly this case - https://github.com/tunguski/interfacer.

It goes through auto generated classes and check do they conform to interfaces found on classpath in specific package. If yes, interface is added to the class. It works with generic interfaces, at least in basic examples I had to deal with.

The plugin is not avro specific, works as a generated code post processor, so it may be used in other cases too.

<!--    post process avro generated sources and add interfaces from package   pl.matsuo.interfacer.showcase to every generated class that has    all methods from specific interface  --> <plugin>     <groupId>pl.matsuo.interfacer</groupId>     <artifactId>interfacer-maven-plugin</artifactId>     <version>0.0.6</version>     <executions>         <execution>             <configuration>                 <interfacesDirectory>${project.basedir}/src/main/java</interfacesDirectory>                 <interfacePackage>pl.matsuo.interfacer.showcase</interfacePackage>             </configuration>             <goals>                 <goal>add-interfaces</goal>             </goals>         </execution>     </executions> </plugin> 
// src/main/java manually defined interface public interface HasName {   String getName(); }  // target/generated-sources/avro public class Person {    String name;    public String getName() {     return name;   }   // [...] }  public class Company {    String name;    public String getName() {     return name;   }   // [...] }  // after this plugin run  // target/generated-sources/avro public class Person implements HasName {    String name;    public String getName() {     return name;   }   // [...] }  public class Company implements HasName {    String name;    public String getName() {     return name;   }   // [...] } 
like image 32
tunguski Avatar answered Sep 22 '22 17:09

tunguski