Avro¶
Overview¶
This document defines the translation methodology for ALFA to Avro 1.9.1 IDL. Some translations will inevitably have not direct corresponding type. However, Avro annotations are used to indicate additional attributes where required to provide information in the Avro IDL schema to be used where necessary.
The Avro exporter continues to be refined in terms of its translation capabilities. Feedback is most welcome on how to improve the translation into Avro, to make the best use of Avro features.
Usage:
alfa -c -e avro -o generated/avro src
For use with the ALFA Maven Plugin, set <exportType>avro</exportType>
in the plugin configuration.
Primitive types¶
ALFA primitives are mapped to Avro as defined in the following table.
ALFA | Avro |
---|---|
binary | bytes |
boolean | boolean |
byte | bytes |
char | string |
date | date |
datetime | timestamp_ms |
decimal | decimal |
decimal(s,p) | decimal(s,p) |
duration | duration |
double | double |
float | float |
int | int |
long | long |
pattern | string |
short | int |
string | string |
time | time_ms |
url | string |
uuid | string |
void | void |
Vectors types¶
Map¶
ALFA map< K, V > where scalar keys are used, are represented as a Avro map< V >. For complex key types, use array of a record tuple. Given an ALFA definition ( assume Person is an existing record ):
record Team {
salaries : map<Person, double>
}
Avro translation will be:
@alfa-synthetic( "map-tuple" )
record __TeamSalariesEntry {
Person key;
double value;
}
record Team {
array< __TeamSalariesEntry > salaries;
}
The @alfa-synthetic( "map-tuple" )
Avro annotation serves as a marker on the record indicate the array< __TeamSalariesEntry >
type, should be treated as a map if the target programming model support non-scalar map keys.
Set¶
ALFA set< T > is represented as an Avro array< T > with an annotation to indicate it should be treated as a set<T>
if
the target programming model supports set<T>
.
record Team {
members : set< Person >
}
Avro translation will be:
record Team {
@alfa-isSet( "true" )
array< Person > members;
}
Seq¶
ALFA seqVectorType translates directly to Avro array< T >.
Enum¶
ALFA supports enum< A, B, C >
directly as a field type instead of having to declare it as a standalone enum< C1, C2, … > type.
Such field type declarations translates to an Avro enum and a reference to that declaration. So given:
record Velocity {
direction : enum< N, S, E, W >
}
Avro translation will be:
record Velocity {
direction : __VelocityDirection;
}
@alfa-synthetic( "true" )
enum __VelocityDirection {
N, S, E, W
}
Tuple¶
An ALFA tuple< :T1, :T2, .. > type translates to an Avro record and and a reference to that declaration. So given:
record Drawing {
resolution : tuple< int, int >
origin : tuple< X : int, Y : int >
}
Avro translation will be ( notice the generated field naming ):
record Drawing {
resolution : __DrawingResolution
origin : __DrawingOrigin
}
@alfa-synthetic( "true" )
record __DrawingResolution {
int _1;
int _2;
}
@alfa-synthetic( "true" )
record __DrawingOrigin {
int X;
int Y;
int Z;
}
Union¶
An ALFA union< :T1, :T2, ..> field type translates naturally to an Avro union field type. So given:
record Result {
value : union< double, int, string >
}
Avro translation will simply be:
record Result {
union{ double, int, string } value;
}
ALFA union field type can optionally have tagged names, therefore can have different tagged names with the same type. Consider:
record Payment {
method : union< Paypal : string, BankAccount : string , Cash : void >
}
The above contains 2 fields of type string, which will not translate as-is to an Avro union. Also a Cash
field of type
void
which is not supported in Avro. When duplicate type or void
is encountered in an ALFA union, those are
wrapped as records in Avro as shown below.
record Payment {
union{ string, __PaymentMethodBankAccount, __PaymentMethodCash } method;
}
record __PaymentMethodBankAccount {
string BankAccount;
}
record __PaymentMethodCash {
}
User-defined types¶
trait¶
An ALFA trait is translated to an Avro union as the ALFA transitive closure of implementations of the trait. Given an ALFA definition:
trait Shape { area : double }
trait Rectangle includes Shape { height : int width : int }
record Square includes Rectangle { }
record Circle includes Shape { radius : int }
record Triangle includes Shape { side : int }
record Drawing { item : Shape }
Avro translation will be the following:
record Square { double area; int height; int width; }
record Circle { double area; int radius; }
record Triangle { double area; int side; }
record Shape {
union { Square, Circle, Triangle } shape;
}
record Rectangle {
union { Square } rectangle;
}
record Drawing { Shape item; }
Notice the ALFA traits are Avro records with unions of its transitive closure from ALFA .
entity¶
An ALFA entity will be translated to a Avro record with a field named __key
appearing as the 1st field.
This field represents the key of the entity, if one is defined ( ALFA permits keyless entity to represent
singletons ).
entity Order key( id : uuid ) {
items : list< Item >
}
Avro translation will be the following:
record OrderKey {
id : string
}
record Order {
OrderKey __key;
array< Item > items;
}
union¶
An ALFA union gets translated to a Avro record with a single union field containing ALFA union fields. This is identical to ALFA in-lined union field type translation.
union Payment {
Paypal : string
BankAccount : string
Cash : void
}
Avro translation will be the following:
record Payment {
union{ string, __PaymentMethodBankAccount, __PaymentMethodCash } method;
}
record __PaymentMethodBankAccount {
string BankAccount;
}
record __PaymentMethodCash {
}
service¶
An ALFA service will be translated to a new Avro Protocol definition file containing the set of methods. All definitions required by the interface will be available in a seperate file containing only Avro records.
Given the ALFA definition:
namespace com.acme
record Order {}
service OrderProcessing {
createEmptyOrder() : Order
saveOrder( order : Order ) : void
}
The 2 following Avro definitions will be generated:
File com.acme.OrderProcessing.avdl
. An Avro Protocol definition per ALFA service.
@namespace ("com.acme")
protocol OrderProcessing
{
import idl "com.acme.avdl";
com.acme.Order createEmptyOrder();
void saveOrder( com.acme.Order order );
}
File com.acme.avdl
. An Avro Protocol definition by ALFA namespace.
@namespace ("com.acme")
protocol com_acme
{
record Order { }
}
Other ALFA features¶
Optional fields¶
Fields marked as optional in ALFA using T?
will be converted to Avro as union{T, null}
.
key ¶
ALFA key<T>
where T
is an entity will be translated to the key declaration of TKey
, where the ALFA key would have been
generated as a Avro record.
Building an ALFA to Avro Project¶
This step can be performed in Maven using the following build configuration. The configuration below will invoke the ALFA compiler to generate Avro, invoke Avro schema to Java generator, and finally compile the generated Java.
<dependencies>
<dependency>
<groupId>org.apache.avro</groupId>
<artifactId>avro</artifactId>
<version>1.9.1</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>1.7</version>
<executions>
<execution>
<id>add-source</id>
<phase>generate-sources</phase>
<goals>
<goal>add-source</goal>
</goals>
<configuration>
<sources>
<source>${project.build.directory}/generated-sources/java</source>
</sources>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>com.schemarise.alfa.utils</groupId>
<artifactId>alfa-maven-plugin</artifactId>
<version>3.4.0</version>
<executions>
<execution>
<id>alfa-build</id>
<goals>
<goal>compile</goal>
<goal>package</goal>
</goals>
<configuration>
<srcPath>${basedir}/src/alfa/</srcPath>
<exportSettings>
<exportSetting>
<exportType>avro</exportType>
</exportSetting>
</exportSettings>
</configuration>
</execution>
</executions>
</plugin>
<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>
<goal>protocol</goal>
<goal>idl-protocol</goal>
</goals>
<configuration>
<sourceDirectory>
${project.build.directory}/generated-sources/avro
</sourceDirectory>
<outputDirectory>
${project.build.directory}/generated-sources/java
</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>