Introduction
This is a Java implementation of Cap’n Proto. It has two main components:
-
A C++ program
capnpc-java
that generates Java source code from Cap’n Proto schemas by acting as a plugin to the Cap’n Proto schema compiler. -
A Java package
org.capnproto
that provides runtime support forcapnpc-java
’s generated code.
These components let you make your data mobile,
so that you can manipulate your data in Java
and also easily communicate it
to distributed components written
in other programming languages.
Under the hood, all operations are
backed by java.nio.ByteBuffer
.
If you, for example, want to communicate over a
network, you can just directly write the bytes to the wire.
There is no encode or decode step!
We hope eventually to provide support in Java for a distributed object-capability layer built on top of this serialization layer, but we have not embarked on that project yet.
Installation
Schema Compiler and Plugin
You will need to install the
latest release of the Cap’n Proto schema compiler.
Then, running make
in the root directory of the capnproto-java repository
should build capnpc-java
.
On Windows, you can instead grab a prebuilt win32 executable from here:
https://dwrensha.ws/capnproto/capnpc-java.exe.zip
For help on how to invoke the schema compiler:
capnp compile --help
Note that you’ll need to include java.capnp
so that you can use the package
and outerClassname
annotations. This schema is located in compiler/src/main/schema
.
You might find useful this Maven Plugin.
Runtime
The runtime is available on The Central Repository.
We use maven for building and testing the Java code.
Running mvn compile
at the top-level directory should build
org.capnproto
and org.capnproto.examples
.
Running mvn test
should run the test suite.
Example
We can define types in a schema like this:
Then, after running the schema compiler, we can then use those types from Java like this:
To read a message:
$ echo '(people = [(id = 123, name = "Alice",' \
'email = "alice@example.com", employment = (school = "MIT"))])' \
| capnp encode --packed examples/src/main/schema/addressbook.capnp \
AddressBook \
| java -cp runtime/target/classes:examples/target/classes \
org.capnproto.examples.AddressbookMain read
To write a message:
$ java -cp runtime/target/classes:examples/target/classes \
org.capnproto.examples.AddressbookMain write \
| capnp decode --packed examples/src/main/schema/addressbook.capnp \
AddressBook
API
The classes and methods provided by the Java runtime and generated code correspond directly to those provided by the C++ implementation, with just a few adjustments.
-
Java does not have unsigned integer types, so a
UInt64
in a schema gets mapped to along
in Java, aUInt32
gets mapped to anint
in Java, and so on. You are responsible for correctly handling arithmetic on values of these types. Note that Java 8 has standard functions that can help with this. -
Because Java generics don’t get monomorphized at compile time like C++ templates do, generic methods need to have an additional factory argument to allow the proper dispatch to occur.
MessageReader.getRoot()
andMessageBuilder.initRoot()
are two examples, as shown above.
Tips
- The main I/O methods in
Serialize
andSerializePacked
are written in terms ofWritableByteChannel
andReadableByteChannel
. You should be very careful if you try to convert ajava.io.OutputStream
to aWritableByteChannel
with thejava.nio.channels.Channels.newChannel()
method. If yourOutputStream
was buffered, the new channel-based wrapper of it will have no way to flush it! Note that the stream returned byProcess.getOutputStream()
is buffered.
Future Work
There’s a lot left to do, and we’d love to have your help! Here are some missing pieces:
- Orphans.
- Dynamic reflection.
- Optimizations, e.g. iterators for
StructList
that update in place instead of allocating for each element. - Improvements for build and packaging, e.g. getting a distribution on Maven.
- The entire object-capability RPC layer.