Introduction to DataKernel for Node.js Developers

This article is for Node.js developers who have basic Java knowledge and intend to continue with Java, looking for an alternative to the intricate modern Java frameworks.

DataKernel is a lightweight asynchronous framework that preserves all the Java advantages and implements Node.js-inspired features.

But first things first. Let’s briefly go through the necessary prerequisites.

If you already have JDK, IDE and Maven installed, you can skip this part and go directly to the Comparison or Getting Started tutorial.

What you will need:

  • JDK 1.8+
  • IDE (might be IntelliJ IDEA)
  • Maven 3.0+

Step-by-step guide

Then you can navigate to Getting Started tutorial and launch a simple HTTP server.

Comparison

Let’s make a concise comparison of Node.js and DataKernel, to give you a better understanding of what is going on.

Core similarities

  • Both are asynchronous
  • Use single-threaded async Promises
  • Run in Event Loop

Core differences

  • Java is a strongly typed programming language, so DataKernel uses this too.
  • Unlike JS, Java doesn’t have async/await special syntax. Instead, DataKernel uses Promises.
  • Since DataKernel preserves the Java advantages, multithreading can be used too. For example, to run several Event Loops in parallel inside one JVM process.

Hello World servers

//Node.js
const http = require('http');

const hostname = '127.0.0.1';
const port = 3000;

const server = http.createServer((req, res) => {
  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/plain');
  res.end('Hello World\n');
});

server.listen(port, hostname, () => {
  console.log(`Server is running`);
});

DataKernel provides two options:

  • server runs in the explicitly provided Event Loop
public static void main(String[] args) throws IOException {
	Eventloop eventloop = Eventloop.create();
	AsyncHttpServer server = AsyncHttpServer.create(eventloop,
			request -> HttpResponse.ok200()
					.withBody(HELLO_WORLD))
			.withListenPort(8080);

	server.listen();

	System.out.println("Server is running");
	System.out.println("You can connect from browser by visiting 'http://localhost:8080/'");

	eventloop.run();
}
  • with the help of the Launcher class that will provide Event Loop automatically.
public final class HttpHelloWorldExample extends HttpServerLauncher {
	@Provides
	AsyncServlet servlet() {
		return request -> HttpResponse.ok200().withPlainText("Hello World");
	}

	public static void main(String[] args) throws Exception {
		Launcher launcher = new HttpHelloWorldExample();
		launcher.launch(args);
	}
}
  • Despite the typical Java concept that regular application is usually a WAR archive that should be deployed to some application server, DataKernel application has embedded server. So it looks like a regular Java program. You just launch it and that’s it - like Node.js.

  • DataKernel supports running Event Loop in the main, like Node.js executes in Event Loop. Moreover, DK provides features like Launcher and DI.

Promises

//JS
var promise = new Promise( function(resolve, reject) {  resolve('Hello World'); } )
//DataKernel
Promise<String> promise = Promise.of("Hello World");
  • JS has several dynamical chaining methods - then(), catch().
doSomething()
      .then(doSomethingElse)
      .catch(handleError)
      .then(doMoreStuff)
      .then(doFinalThing)
      .catch(handleAnotherError)
  • DataKernel provides a wide range of methods - then(), map(), thenEx(), mapEx(), whenResult(), whenException() etc.
doSomeProcess()
		.whenResult(result -> System.out.println(String.format("Result of some process is '%s'", result)))
		.whenException(e -> System.out.println(String.format("Exception after some process is '%s'",e.getMessage())))
		.map(String::toLowerCase)
		.mapEx((result, e) -> e == null ? String.format("The mapped result is '%s'", result) : e.getMessage())
		.whenResult(System.out::println);
  • JS allows to execute promise-ified functions using all() method.
Promise.all(arrayOfPromises)
.then(function(arrayOfResults) {
    /* Do something when all Promises are resolved */
})
.catch(function(err) {
    /* Handle error if any of Promises fails */
})
  • DataKernel provides even more useful methods - combine(), both(), either(), any() etc. Have a look at combine():
Promise<Integer> firstNumber = Promise.of(10);
Promise<Integer> secondNumber = Promises.delay(2000, 100);

Promise<Integer> result = firstNumber.combine(secondNumber, Integer::sum);
result.whenResult(res -> System.out.println("The first result is " + res));

For more examples of these methods, navigate to Promises.

Middleware VS Servlets

  • Express.js allows to use a chain of functions (middlewares) that are called one after the other, make any modifications to request, and then send a response back.
  • DataKernel suggests an alternative named routing servlets.

Compare:

//Express.js
app.use('/', function (req, res, next) {
  res.status(200).send('Hello World!')
})

//DataKernel
RoutingServlet.create()
	.map(GET, "/", request -> Promise.of(
		HttpResponse.ok200().withPlainText("Hello World!")));
  • DK routing servlet allows to simply deal with the application’s endpoints.
  • You can create servlets for each of the specified paths to handle requests independently and preserve modularity.
  • DataKernel uses functional composition, so servlets can be wrapped one in another.
AsyncServlet servlet(AsyncServlet servlet1, AsyncServlet servlet2) {
		return RoutingServlet.create()
				.map("/", $ -> Promise.of(HttpResponse.ok200().withHtml("<a href=\"/first\">first</a><br><a href=\"/second\">second</a>")))
				.map("/first", servlet1)
				.map("/second", servlet2);
	}

	AsyncServlet servlet1() {...}
	AsyncServlet servlet2() {...}
}
  • Whereas Node.js always follows the chaining construct, like:
var app = express()
var router = express.Router()

router.use(function (req, res, next) { ... })

router.use('/first', function (req, res, next) {
  ...
  next()
}, function (req, res, next) {
  ...
  next()
})

router.get('/second', function (req, res, next) { ... })

app.use('/', router)

What’s next?

To make DataKernel more developer-friendly, we’ve created dozens of tutorials and examples of different scales, representing most of the framework’s capabilities. You can also go through tutorials or explore our docs first.