Home » Java » Enterprise Java » KivaKit applications

About Jonathan Locke

Jonathan Locke
Jonathan has been working with Java since 1996, and he was a member of the Sun Microsystems Java Team. As an open source author, he is originator of the Apache Wicket web framework (https://wicket.apache.org), as well as KivaKit (https://www.kivakit.org, @OpenKivaKit) and Lexakai (a tool for producing UML diagrams and Markdown indexes from Java source code, available at https://www.lexakai.org, @OpenLexakai). Jonathan works as a Principal Software Architect at Telenav (https://www.telenav.com), and in the future, Telenav will release further toolkit designed and led by Jonathan called MesaKit, focused on map analysis and navigation.

KivaKit applications

The kivakit-application module contains building blocks for creating applications and servers. In the diagram below, we can see that the Application class extends BaseComponent. Server, in turn, extends Application. BaseComponent inherits Repeater functionality from BaseRepeater, and handy default methods from the Component interface. ComponentMixin (shown in the next diagram) also inherits these methods from Component.

Application provides command-line parsing (see KivaKit command line parsing) and application lifecycle methods. In addition, it inherits functionality from BaseComponent for registering and locating objects and settings (see Kivakit components and settings):

Example

In the following example, we will create an application that counts the lines of its file argument. With no arguments, the application will give detailed help. With the argument -show-file-size=true, it will show the size of the file in bytes.

Application initialization

To start our application running we supply code similar to this:

public class ApplicationExample extends Application
{
    public static void main(String[] arguments)
    {
        new ApplicationExample().run(arguments);
    }
    
    private ApplicationExample()
    {
        super(ApplicationExampleProject());
    }        
    
    [...]
    
    @Override
    protected void onRun()
    {
        [...]
    }        
}

The main() method creates an instance of the application and the constructor for the application passes an instance of Project to the superclass. The application then calls run(), which proceeds to initialize the project and application. When the application is fully initialized and ready to run, the onRun() method is called.

Project initialization

To initialize our application’s project, we create a subclass of Project, which provides any required initialization logic, as well as a set of dependent projects retrieved via the dependencies() method. When the application runs, its project and all of the sub-projects in its project dependency tree will be initialized before onRun() is called:

public class ApplicationExampleProject extends Project
{
    private static Lazy<ApplicationExampleProject> project = 
        Lazy.of(ApplicationExampleProject::new);

    public static ApplicationExampleProject get()
    {
        return project.get();
    }

    protected ApplicationExampleProject()
    {
    }

    @Override
    public Set<Project> dependencies()
    {
        return Set.of(ResourceProject.get());
    }
}

Command line parsing and application logic

Once our example application has initialized, onRun() provides the application logic:

private ArgumentParser<File> INPUT =
        fileArgumentParser("Input text file")
                .required()
                .build();

private SwitchParser<Boolean> SHOW_FILE_SIZE =
        booleanSwitchParser("show-file-size", "Show the file size in bytes")
                .optional()
                .defaultValue(false)
                .build();
                
@Override
public String description()
{
    return "Example application that counts the number of lines" +
           " in the file argument passed to the command line";
}

@Override
protected void onRun()
{
    var input = argument(INPUT);

    if (input.exists())
    {
        showFile(input);
    }
    else
    {
        problem("File does not exist: $", input.path());
    }
}

@Override
protected List<ArgumentParser<?>> argumentParsers()
{
    return List.of(INPUT);
}

@Override
protected Set<SwitchParser<?>> switchParsers()
{
    return Set.of(SHOW_FILE_SIZE);
}

private void showFile(File input)
{
    if (get(SHOW_FILE_SIZE))
    {
        information("File size = $", input.sizeInBytes());
    }

    information("Lines = $", Count.count(input.reader().lines()));
}

When the Application.run() method is called, switch and argument parsers for the application are retrieved from switchParsers() and argumentParsers(), respectively. The logic in Application.run() then uses these parsers to parse the String[] argument that was passed to main() into a CommandLine object.

The onRun() method in our example application calls argument() with the INPUT file argument parser to obtain an input File object from the CommandLine:

var input = argument(INPUT);

Next, if the file exists, our example application calls showFile() to show the number of lines in the file. In that same method, if the boolean switch SHOW_FILE_SIZE is true, it also shows the file size in bytes:

if (get(SHOW_FILE_SIZE))
{
    information("File size = $", input.sizeInBytes());
}

information("Lines = $", Count.count(input.reader().lines()));

Help

Finally, if something goes wrong interpreting our application’s command-line arguments, KivaKit will capture any broadcast messages, as well as information from the argument and switch parsers. It will then use this information to provide detailed help about what went wrong and how to correctly use the application:

┏━━━━━━━━━━━━━━━━━┫ COMMAND LINE ERROR(S) ┣━━━━━━━━━━━━━━━━━┓
┋     ○ Required File argument "Input text file" is missing ┋
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
 

KivaKit 0.9.9-SNAPSHOT (puffy telephone)

Usage: ApplicationExample 0.9.9-SNAPSHOT <switches> <arguments>

Example application that counts the number of lines in the file argument passed to the command line

Arguments:

  1. File (required) - Input text file

Switches:

    Optional:

  -show-file-size=Boolean (optional, default: false) : Show the file size in bytes

Code

The complete code for the example presented here is available in the kivakit-examples repository. The Maven dependency for kivakit-application is:

<dependency>
    <groupId>com.telenav.kivakit</groupId>
    <artifactId>kivakit-application</artifactId>
    <version>${kivakit.version}</version>
</dependency>
Published on Java Code Geeks with permission by Jonathan Locke, partner at our JCG program. See the original article here: KivaKit applications

Opinions expressed by Java Code Geeks contributors are their own.

(0 rating, 0 votes)
You need to be a registered member to rate this.
Start the discussion Views Tweet it!
Do you want to know how to develop your skillset to become a Java Rockstar?
Subscribe to our newsletter to start Rocking right now!
To get you started we give you our best selling eBooks for FREE!
1. JPA Mini Book
2. JVM Troubleshooting Guide
3. JUnit Tutorial for Unit Testing
4. Java Annotations Tutorial
5. Java Interview Questions
6. Spring Interview Questions
7. Android UI Design
and many more ....
I agree to the Terms and Privacy Policy
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments