You are viewing a plain text version of this content. The canonical link for it is here.
Posted to user@avro.apache.org by "ehud.lev" <eh...@gmail.com> on 2014/02/25 14:08:52 UTC
Re: Enum & backward compatibility in distributed services...
Hi,
I am one of the developers in Amichay's group.
I manage to create a small POC that solve this issue.
First I updated the enum template and set it using the avro plugin (I
add enum.vm below), so I got a similar class as you wrote.
In addition, I changed the code of the SpecificDatumReader (added
below). I am pretty sure that it will work with an extension as well.
The code is calling in reflection to the class constructor with the
symbol value even if it does not exist in the expected schema.
I catch the avro exception and replace the string. I know this is quick
and dirty, and I would like to change it in the
ResolvingGrammarGenerator.mkEnumAdjust. However it is a private method,
so either I can extend many classes or change the private method but
then I would ruin the GenericDatumReader.
So here are my questions:
1. Should I added it as an external change and if so, is there a way to
change the adjustments without adding a lot of duplication code.
2. We are currently working with avro 1.7.3. Do you plan to add
something like this to the next versions ? or how can we (after writing
the whole solution) add it ?
Thanks
Ehud
*SpecificDatumReader.java
...
*
/** Called to read an enum value. May be overridden for alternate enum
* representations. By default, returns a GenericEnumSymbol. */
protected Object readEnum(Schema expected, Decoder in) throws
IOException {
try{
List<String> symList = expected.getEnumSymbols();
String symbol = symList.get(in.readEnum());
return createEnum(symbol,expected);
}
// TODO change this
catch(AvroTypeException e){
String tryingSymbol = e.getMessage().replace("No match for",
"").trim();
return createEnum(tryingSymbol, expected);
}
}
@Override
@SuppressWarnings("unchecked")
protected Object createEnum(String symbol, Schema schema) {
Class c = getSpecificData().getClass(schema);
try{
return c.getDeclaredConstructor(Schema.class,
String.class).newInstance(schema,symbol);
} catch (InvocationTargetException e) {
throw new AvroTypeException(String.format("can not read
constructor of class [%s]",c.getCanonicalName()),e);
} catch (NoSuchMethodException e) {
throw new AvroTypeException(String.format("can not read
constructor of class [%s]",c.getCanonicalName()),e);
} catch (InstantiationException e) {
throw new AvroTypeException(String.format("can not read
constructor of class [%s]",c.getCanonicalName()),e);
} catch (IllegalAccessException e) {
throw new AvroTypeException(String.format("can not read
constructor of class [%s]",c.getCanonicalName()),e);
}
}
*enum.vm*
#if ($schema.getNamespace())
package $schema.getNamespace();
#end
import org.apache.avro.Schema;
import org.apache.avro.generic.GenericData;
@SuppressWarnings("all")
#if ($schema.getDoc())
/** $schema.getDoc() */
#end
#foreach ($annotation in $this.javaAnnotations($schema))
@$annotation
#end
@org.apache.avro.specific.AvroGenerated
public class ${this.mangle($schema.getName())} extends
GenericData.EnumSymbol {
public ${this.mangle($schema.getName())}(Schema schema, String symbol) {
super(schema, symbol);
}
public static final org.apache.avro.Schema SCHEMA$ = new
org.apache.avro.Schema.Parser().parse("${this.javaEscape($schema.toString())}");
#foreach ($symbol in ${schema.getEnumSymbols()})
/*Ehuds Genration*/
public static final ${this.mangle($schema.getName())}
${this.mangle($symbol)} = new
${this.mangle($schema.getName())}(SCHEMA$,"${this.mangle($symbol)}");
#end
public static final ${this.mangle($schema.getName())} [] values = new
${this.mangle($schema.getName())} [] {
#foreach ($symbol in
${schema.getEnumSymbols()})${this.mangle($symbol)}#if
($velocityHasNext), #end#end
};
}