Avro is surprisingly difficult to get into, as it is lacking the most basic "getting started" documentation for a new-comer to the project. This post serves as a reminder to myself of what I did, and hopefully to help others get the hello world working quickly. If people find it useful, let's fill it out and submit it to the Avro wiki!
Prerequisites: knowledge of Apache Maven
Start by adding the Avro maven plugin to the pom. This is needed to compile the Avro schema definitions into the Java classes.
<plugin> <groupId>org.apache.avro</groupId> <artifactId>avro-maven-plugin</artifactId> <version>1.5.1</version> <executions> <execution> <id>schemas</id> <phase>generate-sources</phase> <goals> <goal>schema</goal> <goal>protocol</goal> <goal>idl-protocol</goal> </goals> <configuration> <excludes> <exclude>**/mapred/tether/**</exclude> </excludes> <sourceDirectory>${project.basedir}/src/main/avro/</sourceDirectory> <outputDirectory>${project.basedir}/src/main/java/</outputDirectory> <testSourceDirectory>${project.basedir}/src/test/avro/</testSourceDirectory> <testOutputDirectory>${project.basedir}/src/test/java/</testOutputDirectory> </configuration> </execution> </executions> </plugin>
Now add the dependency on Avro and the Avro IPC (Inter Process Calls)
<dependency> <groupId>org.apache.avro</groupId> <artifactId>avro</artifactId> <version>1.5.1</version> </dependency> <dependency> <groupId>org.apache.avro</groupId> <artifactId>avro-ipc</artifactId> <version>1.5.1</version> </dependency>
Now we create the Avro Protocol file, which defines the RPC exchange. This file is stored in /src/main/ avro/nublookup.avpr and looks like so:
{"namespace": "org.gbif.ecat.ws", "protocol": "NubLookup", "types": [ {"name": "Request", "type": "record", "fields": [ {"name": "kingdom", "type": ["string", "null"]}, {"name": "phylum", "type": ["string", "null"]}, {"name": "class", "type": ["string", "null"]}, {"name": "order", "type": ["string", "null"]}, {"name": "family", "type": ["string", "null"]}, {"name": "genus", "type": ["string", "null"]}, {"name": "name", "type": ["string", "null"]} ] }, {"name": "Response", "type": "record", "fields": [ {"name": "kingdomId", "type": ["int", "null"]}, {"name": "phylumId", "type": ["int", "null"]}, {"name": "classId", "type": ["int", "null"]}, {"name": "orderId", "type": ["int", "null"]}, {"name": "familyId", "type": ["int", "null"]}, {"name": "genusId", "type": ["int", "null"]}, {"name": "nameId", "type": ["int", "null"]} ] } ], "messages": { "send": { "request": [{"name": "request", "type": "Request"}], "response": "Response" } } }
This protocol defines an interface called NubLookup, that takes a Request and returns a Response. Simple stuff.
From the command line issue a compile:
$mvn compileThis will generate into src/main/java and the package I declared in the .avpr file (org.gbif.ecat.ws in my case).
Now we can test it using a simple Netty server which is included in the Avro dependency:
public class Test { private static NettyServer server; // A mock implementation public static class NubLookupImpl implements NubLookup { public Response send(Request request) throws AvroRemoteException { Response r = new Response(); r.kingdomId=100; return r; } } public static void main(String[] args) throws IOException { server = new NettyServer(new SpecificResponder( NubLookup.class, new NubLookupImpl()), new InetSocketAddress(7001)); NettyTransceiver client = new NettyTransceiver( new InetSocketAddress(server.getPort())); NubLookup proxy = (NubLookup) SpecificRequestor.getClient(NubLookup.class, client); Request req = new Request(); req.name = new Utf8("Puma"); System.out.println("Result: " + proxy.send(req).kingdomId); client.close(); server.close(); } }
I am evaluating Avro to provide the high performance RPC chatter for lookup services while we process the content for the portal. I'll blog later about the performance compared to the Jersey REST implementation currently running.