Quantcast
Channel: Baeldung
Viewing all articles
Browse latest Browse all 4535

Introducing KivaKit

$
0
0

1. Overview

KivaKit is a modular Java application framework designed to make developing microservices and applications quicker and easier. KivaKit has been developed at Telenav since 2011. It is now available as an Apache Licensed, Open Source project on GitHub.

In this article, we'll explore the design of KivaKit as a collection of “mini-frameworks” that work together. In addition, we'll take a look at the essential features of each mini-framework.

2. KivaKit Mini-frameworks

Looking in the kivakit and kivakit-extensions repositories, we can see that KivaKit 1.0 contains 54 modules. We could find this overwhelming. However, if we take things one step at a time, it's not so bad. For starters, we can pick and choose what we want to include in our projects. Each module in KivaKit is designed to be used on its own.

Some KivaKit modules contain mini-frameworks. A mini-framework is a simple, abstract design that addresses a common problem. If we examine KivaKit's mini-frameworks, we will find that they have straightforward, broadly applicable interfaces. As a result, they are a bit like Legos™. That is to say, they are simple pieces that snap together.

Here, we can see KivaKit's mini-frameworks and how they relate to each other:

 

 

Mini-framework Module Description
Application kivakit-application Base components for applications and servers
Command-Line Parsing kivakit-commandline Switch and argument parsing using the conversion and validation mini-frameworks
Component kivakit-component Base functionality for implementing KivaKit components, including applications
Conversion kivakit-kernel An abstraction for implementing robust, modular type converters
Extraction kivakit-kernel Extraction of objects from a data source
Interfaces kivakit-kernel Generic interfaces used as integration points between frameworks
Logging kivakit-kernel
kivakit-logs-*
Core logging functionality, log service provider interface (SPI), and log implementations
Messaging kivakit-kernel Enables components to transmit and receive status information
Mixins kivakit-kernel An implementation of stateful traits
Resource kivakit-resource
kivakit-network-*
kivakit-filesystems-*
Abstractions for files, folders, and streamed resources
Service Locator kivakit-configuration An implementation of the service locator pattern for finding components and settings information
Settings kivakit-configuration Provides easy access to component configuration information
Validation kivakit-kernel Base functionality for checking the consistency of objects

We can find these frameworks in the kivakit repository. On the other hand, we will find less important modules like service providers in kivakit-extensions.

2.1. Messaging

As the diagram above shows, messaging is the central point of integration. In KivaKit, messaging formalizes status reporting. As Java programmers, we're used to status information being logged. Sometimes, we also see this information returned to callers or thrown as exceptions. By contrast, status information in KivaKit is contained in Messages. We can write components that broadcast these messages. Also, we can write components that listen to them.

We can see that this design allows components to focus on reporting status consistently. To a component, where status messages go is unimportant. In one case, we might direct messages to a Logger. In another, we might include them in a statistic. We could even display them to an end-user. The component doesn't care. It just reports issues to whoever might be interested.

KivaKit components that broadcast messages can be connected to one or more message repeaters, forming a listener chain:

 

In KivaKit, an Application is usually the end of a listener chain. Therefore, the Application class logs any messages it receives. Along the way, components in the listener chain may have other uses for these messages.

2.2. Mixins

Another integration feature of KivaKit is the mixins mini-framework. KivaKit mixins allow base classes to be “mixed in” to types through interface inheritance.  Sometimes, mixins are known as “stateful traits“.

For example, the BaseComponent class in KivaKit provides a foundation for building components. BaseComponent provides convenience methods for sending messages. In addition, it offers easy access to resources, settings, and registered objects.

But we quickly run into a problem with this design. As we know, in Java, a class that already has a base class cannot also extend BaseComponent. KivaKit mixins allow BaseComponent functionality to be added to a component that already has a base class. For example:

public class MyComponent extends MyBaseClass implements ComponentMixin { [...] }

We can see here that the interface ComponentMixin extends both Mixin and Component:

 

The Mixin interface provides a state() method to ComponentMixin. First, this method is used to create and associate a BaseComponent with the object implementing ComponentMixin. Second, ComponentMixin implements each method of the Component interface as a Java default method. Third, each default method delegates to the associated BaseComponent. In this way, implementing ComponentMixin provides the same methods as extending BaseComponent.

2.3. Service Locator

The service locator class Registry allows us to wire components together. A Registry provides roughly the same functionality as dependency injection (DI). However, it differs from the typical use of DI in one important way. In the service locator pattern, components reach out for the interfaces that they need. On the other hand, DI pushes interfaces into components. As a result, the service locator approach improves encapsulation. It also reduces the scope of references. For example, Registry can be used neatly within a method:

class MyData extends BaseComponent {
    [...]
    public void save() {
        var database = require(Database.class);
        database.save(this);
    }
}

The BaseComponent.require(Class) method here looks up objects in a Registry. When our save() method returns, the database reference leaves scope. This ensures that no external code can obtain our reference.

When our application starts, we can register service objects with one of the BaseComponent.registerObject() methods. Later, code elsewhere in our application can look them up with require(Class).

2.4. Resources and Filesystems

The kivakit-resource module provides abstractions for reading and writing streamed resources and accessing filesystems. We can see here a few of the more important resource types that KivaKit includes:

  • Files (local, Zip, S3, and HDFS)
  • Package resources
  • Network protocols (sockets, HTTP, HTTPS, and FTP)
  • Input and output streams

We get two valuable benefits from this abstraction. We can:

2.5. Components

The kivakit-component module gives us ready access to common functionality. We can:

  • Send and receive messages
  • Access packages and packaged resources
  • Register and lookup objects, components, and settings

The Component interface is implemented by both BaseComponent and ComponentMixin. As a result, we can add “component nature” to any object.

2.6. Logging

Listener chains formed by KivaKit components often terminate in a Logger. A Logger writes the messages it receives to one or more Logs. In addition, the kivakit-kernel module provides a service provider interface (SPI) for implementing Logs. We can see the full design of the logging mini-framework in UML here.

Using the logging SPI, the kivakit-extensions repository provides us with some Log implementations:

Provider Module
ConsoleLog kivakit-kernel
FileLog kivakit-logs-file
EmailLog kivakit-logs-email

One or more logs can be selected and configured from the command line. This is done by defining the KIVAKIT_LOG system property.

2.7. Conversion and Validation

The kivakit-kernel module contains mini-frameworks for type conversion and object validation. These frameworks are integrated with KivaKit messaging. This allows them to report problems consistently. It also simplifies usage. To implement a type Converter like a StringConverter, we need to write the conversion code. We don't need to worry about exceptions, empty strings, or null values.

 

We can see converters in use in many places in KivaKit, including:

  • Switch and argument parsing
  • Loading settings objects from properties files
  • Formatting objects as debug strings
  • Reading objects from CSV files

Messages broadcast by Validatables are captured by the validation mini-framework. Subsequently, they are analyzed to provide us with easy access to error statistics and validation problems.

 

2.8. Applications, Command Lines, and Settings

The kivakit-application, kivakit-configuration, and kivakit-commandline modules provide a simple, consistent model for developing applications.

The kivakit-application project supplies the Application base class. An Application is a Component. It provides settings information using kivakit-configuration. In addition, it provides command-line parsing with kivakit-commandline.

The kivakit-configuration project uses the kivakit-resource module to load settings information from .properties resources (and other sources in the future). It converts the properties in these resources to objects using kivakit-kernel converters. The converted objects are then validated with the validation mini-framework.

Command-line arguments and switches for an application are parsed by the kivakit-commandline module using KivaKit converters and validators. We can see issues that arise in the application's ConsoleLog.

 

2.9. Microservices

So far, we've discussed KivaKit features that are generally useful to any application. In addition, KivaKit also provides functionality in kivakit-extensions that is explicitly targeted at microservices. Let's take a quick look at kivakit-web.

The kivakit-web project contains modules for rapidly developing a simple REST and web interface to a microservice. The JettyServer class provides us with a way to plug in servlets and filters with a minimum of hassle. Plugins that we can use with JettyServer include:

Plugin Description
JettyJersey REST application support
JettySwagger Swagger automatic REST documentation
JettyWicket Support for the Apache Wicket web framework

These plugins can be combined to provide a RESTful microservice with Swagger documentation and a web interface:

var application = new MyRestApplication();
listenTo(new JettyServer())
    .port(8080)
    .add("/*", new JettyWicket(MyWebApplication.class))
    .add("/open-api/*", new JettySwaggerOpenApi(application))
    .add("/docs/*", new JettySwaggerIndex(port))
    .add("/webapp/*", new JettySwaggerStaticResources())
    .add("/webjar/*", new JettySwaggerWebJar(application))
    .add("/*", new JettyJersey(application))
    .start();

KivaKit 1.1 will include a dedicated microservices mini-framework. This will make it even easier for us to build microservices.

3. Documentation and Lexakai

The documentation for KivaKit is generated by Lexakai. Lexakai creates UML diagrams (guided by annotations when desired) and updates README.md markdown files. In the readme file for each project, Lexakai updates a standard header and footer. In addition, it maintains indexes for the generated UML diagrams and Javadoc documentation. Lexakai is an open-source project distributed under Apache License.

4. Building KivaKit

KivaKit targets a Java 11 or higher virtual machine (but can be used from Java 8 source code). We can find all the artifacts for KivaKit modules on Maven Central. However, we might want to modify KivaKit or contribute to the open-source project. In this case, we'll need to build it.

To get started, let's set up Git, Git Flow, Java 16 JDK, and Maven 3.8.1 or higher.

First, we clone the kivakit repository into our workspace:

mkdir ~/Workspace
cd ~/Workspace
git clone --branch develop https://github.com/Telenav/kivakit.git

Next, we copy the sample bash profile to our home folder:

cp kivakit/setup/profile ~/.profile

Then we modify ~/.profile to point to our workspace, and our Java and Maven installations:

export KIVAKIT_WORKSPACE=$HOME/Workspace 
export JAVA_HOME=/Library/Java/JavaVirtualMachines/temurin-16.jdk/Contents/Home 
export M2_HOME=$HOME/Developer/apache-maven-3.8.2

After our profile is set up, we ensure that we are running bash (on macOS, zsh is the default now):

chsh -s /bin/bash

And finally, we restart our terminal program and execute the command:

$KIVAKIT_HOME/setup/setup.sh

The setup script will clone kivakit-extensions and some other related repositories. Afterward, it will initialize git-flow and build all our KivaKit projects.

5. Conclusion

In this article, we took a brief look at the design of KivaKit. We also toured some of the more important functions it provides. KivaKit is ideally suited for developing microservices. It has been designed to be learned and used in easy-to-digest, independent pieces.

       

Viewing all articles
Browse latest Browse all 4535

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>