CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-apache-avro--avro-ipc

Avro inter-process communication components providing RPC framework with multiple transport mechanisms and protocol implementations

Pending
Overview
Eval results
Files

protocols.mddocs/

Protocol Implementations

Apache Avro IPC provides three distinct protocol implementations to support different Java object models and development workflows: Generic (no code generation), Specific (generated classes), and Reflect (existing interfaces).

Capabilities

Generic Protocol

Generic protocol implementation works with Avro's GenericData model, requiring no code generation. Uses GenericRecord and other generic types for data representation.

Generic Requestor

public class GenericRequestor extends Requestor {
    // Constructors
    public GenericRequestor(Protocol protocol, Transceiver transceiver) throws IOException;
    public GenericRequestor(Protocol protocol, Transceiver transceiver, GenericData data) throws IOException;
    
    // Data model access
    public GenericData getGenericData();
    
    // Inherited from Requestor
    public Object request(String messageName, Object request) throws Exception;
    public <T> void request(String messageName, Object request, Callback<T> callback) throws Exception;
}

Generic Responder

public abstract class GenericResponder extends Responder {
    // Constructors
    public GenericResponder(Protocol local);
    public GenericResponder(Protocol local, GenericData data);
    
    // Data model access
    public GenericData getGenericData();
    
    // Abstract method - implement your business logic
    public abstract Object respond(Message message, Object request) throws Exception;
    
    // Protected methods for customization
    protected DatumWriter<Object> getDatumWriter(Schema schema);
    protected DatumReader<Object> getDatumReader(Schema actual, Schema expected);
}

Usage Examples

// Generic client setup
Protocol protocol = Protocol.parse(protocolJson);
HttpTransceiver transceiver = new HttpTransceiver(serverUrl);
GenericRequestor requestor = new GenericRequestor(protocol, transceiver);

// Create generic request data
GenericData.Record request = new GenericData.Record(requestSchema);
request.put("message", "Hello World");
request.put("timestamp", System.currentTimeMillis());

// Make RPC call
Object response = requestor.request("echo", request);
if (response instanceof GenericData.Record) {
    GenericData.Record record = (GenericData.Record) response;
    System.out.println("Response: " + record.get("result"));
}

// Generic server implementation
public class MyGenericResponder extends GenericResponder {
    public MyGenericResponder(Protocol protocol) {
        super(protocol);
    }
    
    @Override
    public Object respond(Message message, Object request) throws Exception {
        String messageName = message.getName();
        GenericData.Record requestRecord = (GenericData.Record) request;
        
        switch (messageName) {
            case "echo":
                return handleEcho(requestRecord);
            case "getData":
                return handleGetData(requestRecord);
            default:
                throw new AvroRuntimeException("Unknown message: " + messageName);
        }
    }
    
    private Object handleEcho(GenericData.Record request) {
        GenericData.Record response = new GenericData.Record(responseSchema);
        response.put("result", request.get("message"));
        return response;
    }
}

Specific Protocol

Specific protocol implementation works with Java classes generated from Avro schemas or IDL files. Provides type-safe, compile-time checked RPC interfaces.

Specific Requestor

public class SpecificRequestor extends Requestor implements InvocationHandler {
    // Constructors
    public SpecificRequestor(Class<?> iface, Transceiver transceiver) throws IOException;
    public SpecificRequestor(Class<?> iface, Transceiver transceiver, SpecificData data) throws IOException;
    public SpecificRequestor(Protocol protocol, Transceiver transceiver, SpecificData data) throws IOException;
    protected SpecificRequestor(Protocol protocol, Transceiver transceiver) throws IOException;
    
    // Data model access
    public SpecificData getSpecificData();
    
    // InvocationHandler for proxy clients
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
    
    // Static factory methods for proxy clients
    public static <T> T getClient(Class<T> iface, Transceiver transceiver) throws IOException;
    public static <T> T getClient(Class<T> iface, Transceiver transceiver, SpecificData data) throws IOException;
    public static <T> T getClient(Class<T> iface, SpecificRequestor requestor);
    
    // Utility methods
    public static Protocol getRemote(Object proxy);
    
    // Protected methods for customization
    protected DatumWriter<Object> getDatumWriter(Schema schema);
    protected DatumReader<Object> getDatumReader(Schema writer, Schema reader);
}

Specific Responder

public class SpecificResponder extends GenericResponder {
    // Constructors
    public SpecificResponder(Class iface, Object impl);
    public SpecificResponder(Protocol protocol, Object impl);
    public SpecificResponder(Class iface, Object impl, SpecificData data);
    public SpecificResponder(Protocol protocol, Object impl, SpecificData data);
    
    // Data model access
    public SpecificData getSpecificData();
    
    // Inherited abstract method - automatically implemented via reflection
    public Object respond(Message message, Object request) throws Exception;
    
    // Protected methods for customization
    protected DatumWriter<Object> getDatumWriter(Schema schema);
    protected DatumReader<Object> getDatumReader(Schema actual, Schema expected);
}

Usage Examples

// Assuming generated interface from Avro IDL:
// interface MyService {
//   string echo(string message);
//   UserRecord getUser(long userId);
// }

// Specific client with proxy interface
HttpTransceiver transceiver = new HttpTransceiver(serverUrl);
MyService client = SpecificRequestor.getClient(MyService.class, transceiver);

// Type-safe RPC calls
String response = client.echo("Hello World");
UserRecord user = client.getUser(12345L);

// Alternative: Direct SpecificRequestor usage
SpecificRequestor requestor = new SpecificRequestor(MyService.class, transceiver);
String response = (String) requestor.request("echo", "Hello World");

// Specific server implementation
public class MyServiceImpl implements MyService {
    @Override
    public String echo(String message) {
        return "Echo: " + message;
    }
    
    @Override  
    public UserRecord getUser(long userId) {
        UserRecord user = new UserRecord();
        user.setId(userId);
        user.setName("User " + userId);
        return user;
    }
}

// Server setup
MyServiceImpl implementation = new MyServiceImpl();
SpecificResponder responder = new SpecificResponder(MyService.class, implementation);
SaslSocketServer server = new SaslSocketServer(responder, new InetSocketAddress(8080));
server.start();

Reflect Protocol

Reflect protocol implementation works with existing Java interfaces using reflection, without requiring code generation from Avro schemas.

Reflect Requestor

public class ReflectRequestor extends SpecificRequestor {
    // Constructors
    public ReflectRequestor(Class<?> iface, Transceiver transceiver) throws IOException;
    public ReflectRequestor(Class<?> iface, Transceiver transceiver, ReflectData data) throws IOException; 
    public ReflectRequestor(Protocol protocol, Transceiver transceiver, ReflectData data) throws IOException;
    protected ReflectRequestor(Protocol protocol, Transceiver transceiver) throws IOException;
    
    // Data model access
    public ReflectData getReflectData();
    
    // Static factory methods for proxy clients
    public static <T> T getClient(Class<T> iface, Transceiver transceiver) throws IOException;
    public static <T> T getClient(Class<T> iface, Transceiver transceiver, ReflectData reflectData) throws IOException;
    public static <T> T getClient(Class<T> iface, ReflectRequestor rreq);
    
    // Protected methods for customization
    protected DatumWriter<Object> getDatumWriter(Schema schema);
    protected DatumReader<Object> getDatumReader(Schema writer, Schema reader);
}

Reflect Responder

public class ReflectResponder extends SpecificResponder {
    // Constructors
    public ReflectResponder(Class iface, Object impl);
    public ReflectResponder(Protocol protocol, Object impl);
    public ReflectResponder(Class iface, Object impl, ReflectData data);
    public ReflectResponder(Protocol protocol, Object impl, ReflectData data);
    
    // Data model access
    public ReflectData getReflectData();
    
    // Inherited methods from SpecificResponder
    public Object respond(Message message, Object request) throws Exception;
    
    // Protected methods for customization  
    protected DatumWriter<Object> getDatumWriter(Schema schema);
    protected DatumReader<Object> getDatumReader(Schema actual, Schema expected);
}

Usage Examples

// Existing Java interface (no Avro generation needed)
public interface Calculator {
    double add(double a, double b);
    double subtract(double a, double b);
    Complex multiply(Complex a, Complex b);
}

public class Complex {
    private double real;
    private double imaginary;
    
    // Constructors, getters, setters
    public Complex() {}
    public Complex(double real, double imaginary) {
        this.real = real; 
        this.imaginary = imaginary;
    }
    // ... getters and setters
}

// Reflect client
HttpTransceiver transceiver = new HttpTransceiver(serverUrl);
Calculator client = ReflectRequestor.getClient(Calculator.class, transceiver);

// Type-safe calls using existing interface
double sum = client.add(3.14, 2.86);
Complex result = client.multiply(new Complex(1, 2), new Complex(3, 4));

// Reflect server implementation
public class CalculatorImpl implements Calculator {
    @Override
    public double add(double a, double b) {
        return a + b;
    }
    
    @Override
    public double subtract(double a, double b) {
        return a - b;
    }
    
    @Override
    public Complex multiply(Complex a, Complex b) {
        double real = a.getReal() * b.getReal() - a.getImaginary() * b.getImaginary();
        double imaginary = a.getReal() * b.getImaginary() + a.getImaginary() * b.getReal();
        return new Complex(real, imaginary);
    }
}

// Server setup
CalculatorImpl implementation = new CalculatorImpl();
ReflectResponder responder = new ReflectResponder(Calculator.class, implementation);
SaslSocketServer server = new SaslSocketServer(responder, new InetSocketAddress(8080));
server.start();

Protocol Selection Guidelines

Generic Protocol

  • Use when: Maximum flexibility needed, dynamic schema handling, no code generation desired
  • Pros: No code generation, dynamic schemas, schema evolution flexibility
  • Cons: No compile-time type safety, verbose data construction, runtime type checking
  • Best for: Schema registries, generic proxy services, administrative tools

Specific Protocol

  • Use when: Strong typing desired, performance important, schemas are stable
  • Pros: Compile-time type safety, best performance, clean API
  • Cons: Requires code generation, schema changes need recompilation
  • Best for: Production services, well-defined APIs, high-performance applications

Reflect Protocol

  • Use when: Existing interfaces, no schema/IDL files, rapid prototyping
  • Pros: Works with existing code, no IDL files needed, quick setup
  • Cons: Limited schema evolution, reflection overhead, less optimized
  • Best for: Legacy integration, prototyping, existing Java interfaces

Advanced Usage

Custom Data Models

Each protocol implementation allows custom data models for specialized serialization:

// Custom SpecificData with custom serialization
SpecificData customData = new SpecificData() {
    @Override
    protected Schema createSchema(Type type, Map<String, Schema> names) {
        // Custom schema creation logic
        return super.createSchema(type, names);
    }
};

SpecificRequestor requestor = new SpecificRequestor(MyService.class, transceiver, customData);

Protocol Evolution

Handle schema evolution gracefully:

// Server supporting multiple protocol versions
public class VersionedResponder extends SpecificResponder {
    public VersionedResponder(Class iface, Object impl) {
        super(iface, impl);
    }
    
    @Override
    public Object respond(Message message, Object request) throws Exception {
        Protocol remote = getRemote();
        String version = remote.getProp("version");
        
        if ("1.0".equals(version)) {
            return handleV1Request(message, request);
        } else if ("2.0".equals(version)) {
            return handleV2Request(message, request);
        }
        
        return super.respond(message, request);
    }
}

Error Handling

Each protocol implementation provides consistent error handling:

try {
    String result = client.processData(data);
} catch (AvroRemoteException e) {
    // Application-level exception thrown by remote method
    if (e.getValue() instanceof ValidationError) {
        ValidationError validationError = (ValidationError) e.getValue();
        System.err.println("Validation failed: " + validationError.getMessage());
    }
} catch (IOException e) {
    // Transport or serialization error
    System.err.println("Communication error: " + e.getMessage());
}

Asynchronous Usage

All protocol implementations support asynchronous operations:

// Asynchronous call with callback
requestor.request("processLargeData", data, new Callback<ProcessingResult>() {
    @Override
    public void handleResult(ProcessingResult result) {
        System.out.println("Processing completed: " + result.getStatus());
    }
    
    @Override
    public void handleError(Throwable error) {
        System.err.println("Processing failed: " + error.getMessage());
    }
});

Performance Considerations

Protocol Performance Characteristics

  1. Specific Protocol: Fastest due to optimized serialization and no reflection
  2. Generic Protocol: Moderate performance, schema-based optimizations
  3. Reflect Protocol: Slowest due to reflection overhead

Optimization Tips

// Reuse requestor instances
SpecificRequestor requestor = new SpecificRequestor(MyService.class, transceiver);
// Use same requestor for multiple calls

// Pre-warm reflection for ReflectRequestor
ReflectData reflectData = ReflectData.get();
reflectData.getSchema(MyClass.class); // Pre-compute schema

// Configure data model for performance
SpecificData.get().addLogicalTypeConversion(new MyCustomConversion());

Install with Tessl CLI

npx tessl i tessl/maven-org-apache-avro--avro-ipc

docs

async.md

core-rpc.md

index.md

plugins.md

protocols.md

stats.md

transports.md

tile.json