Skip to main content
Version: 0.9

Bootstrapping

In the previous section, we learned how to use the DrasylNode class β€” a minimalist interface to integrate drasyl into your application. It provides rich flexibility through customization, configuring the node that best fits your use case. But for some use cases, you may want to customize your node even further. For this, we provide a Bootstrapping interface, a more powerful way to create a drasyl node.

To help understand the Bootstrapping interface, we first want to give the background some information on the technical internals of drasyl and the applied concepts.

Background: Netty Concepts​

The core of drasyl is built with Netty. Netty describes itself as "an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients". To this end, Netty defines an architectural model and a rich set of design patterns for network programming.

Channels​

One of Netty's primary building block is the Channel. A Channel is a basic construct of Java NIO. It represents

an open connection to an entity such as a hardware device, a file, a network socket, or a program component that is capable of performing one or more distinct I/O operations, for example reading or writing.

Netty uses this Channel interface mainly for IP-based transports like TCP or UDP. drasyl has adopted this concept and provides a unified interface for communication with peers, regardless of their location and route.

Events Handlers​

Netty (and therefore drasyl) uses distinct events to notify your application about status changes of the channel or issues operations. This allows your application to react with the appropriate action based on the type of occurring event (e.g., logging, data transformation, flow-control, business logic, etc.).

To control what actions your application should apply, each Channel applies the interceptor design pattern. That means that you can register a given number of interceptors (so-called ChannelHandlers) to a channel, performing independently various. These ChannelHandlers can be added, removed, and resorted to at any time for each Channel. Netty provides an extensive set of predefined handlers, most of which are compatible with drasyl!

Bootstrapping​

Bootstrapping defines the startup code configuring the Channel. At a minimum, it binds the node to a given overlay identity on which it will listen for connection requests.

To learn more about Netty, the used concepts, and how to use them, we recommend reading the Netty User Guide as well as the book "Netty in Action" by Marvin Wolfthal and Norman Maurer.

Create Node using Bootstrapping​

First, we need to create a ServerBootstrap object that describes the behavior of our drasyl node. The TraversingDrasylServerChannelInitializer is a special ChannelHandler that conveniently populates other handlers necessary for the minimal operation of a drasyl node. The implementation of ChannelInitializer in the following line defines how to handle received data from other peers. In this case, they are interpreted and output as a string.

Bootstrapping.class
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import org.drasyl.channel.*;
import org.drasyl.identity.Identity;
import static java.nio.charset.StandardCharsets.UTF_8;

public class Bootstrapping {
public static void main(final String[] args) {
final Identity identity = /* code */;

final NioEventLoopGroup group = new NioEventLoopGroup();
final ServerBootstrap b = new ServerBootstrap()
// we want to create a drasyl-based channel (not UDP or TCP).
.channel(DrasylServerChannel.class)
// create and assign a thread pool dedicated to proccess in- and outbound data.
.group(group)
// ChannelHandler in charge of performing all control plane-related operations.
// There is only one server channel per node.
.handler(new TraversingDrasylServerChannelInitializer(identity))
// ChannelHandler in charge of performing all data plane-related operations.
// There is a child channel for each peer.
.childHandler(new ChannelInitializer<DrasylChannel>() {
@Override
protected void initChannel(final DrasylChannel ch) {
final ChannelPipeline p = ch.pipeline();

p.addLast(new SimpleChannelInboundHandler<ByteBuf>() {
@Override
protected void channelRead0(final ChannelHandlerContext ctx,
final ByteBuf msg) {
System.out.println("Got `" + msg.toString(UTF_8) + "` from `" + ctx.channel().remoteAddress() + "`");
}
});
}
});
}
}

Now that the "recipe" for our drasyl node has been defined, we can start it and wait for messages to arrive.

Bootstrapping.class
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import org.drasyl.channel.*;
import org.drasyl.identity.Identity;
import static java.nio.charset.StandardCharsets.UTF_8;

public class Bootstrapping {
public static void main(final String[] args) {
/* code */

try {
// create new node with given identity.
final Channel ch = b.bind(identity.getAddress()).syncUninterruptibly().channel();
System.out.println("Node listening on address " + ch.localAddress());
// wait for node to stop.
ch.closeFuture().awaitUninterruptibly();
}
finally {
// ensure that thread pool is shutdown
group.shutdownGracefully();
}
}
}

Example​

A fully working example can be found here: EchoServerBootstrap