BYO: Building a wc CLI Tool with Java and GraalVM 21

Arpit RathoreArpit Rathore
3 min read

Building a wc CLI Tool with Java and GraalVM 21

Creating command-line tools with Java can be both powerful and efficient, especially when paired with GraalVM's ability to compile Java applications into native executables. In this blog post, we’ll build a simple wc (word count) CLI tool in Java that counts lines, words, and characters in a file. We’ll then use GraalVM 21 to compile it into a native executable for faster startup and lower memory usage.

Overview of the wc Tool

The wc command in Unix-like systems outputs the number of lines, words, and characters in a file. Our Java implementation will replicate this functionality and support file input.


Step 1: Writing the Java Code

Here’s the complete Java code for the wc tool:

package com.arpitrathore.byo;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;

/**
 * Word count
 */
public class App {

  public static void main(String[] args) {
    if (args.length < 1) {
      System.out.println("Usage: java WC <filename>");
      System.exit(1);
    }

    String filePath = args[0];

    try {
      String content = Files.readString(Paths.get(filePath));
      long lineCount = content.lines().count() - 1; //wc actually counts number of new line character
      long wordCount = Arrays.stream(content.split("\\s+")).filter(word -> !word.isBlank()).count();
      long charCount = content.length();

      System.out.printf("%7d %7d %7d %s%n", lineCount, wordCount, charCount, filePath);
    } catch (IOException e) {
      System.err.println("Error reading file: " + e.getMessage());
      System.exit(1);
    }
  }
}

Key Features of the Code

  • File Reading: The Files.readString method reads the file content as a single string.

  • Line Count: The content.lines() method streams the lines and counts them.

  • Word Count: Splits the content by whitespace and filters out empty strings.

  • Character Count: Simply uses the length() method on the string.

  • Unix-like Output: Outputs results in the format: lines words characters filename.


Step 2: Compiling and Running the Program with Maven

We’ll use Maven for dependency management and to build the project. Additionally, we’ll use the native-maven-plugin to create a native executable.

Maven Project Setup

Create a pom.xml file for your project:

<?xml version="1.0" encoding="UTF-8"?>
<project
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"
  xmlns="http://maven.apache.org/POM/4.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.arpitrathore.byo</groupId>
  <artifactId>02-wc</artifactId>
  <version>1.0-SNAPSHOT</version>

  <properties>
    <maven.compiler.source>21</maven.compiler.source>
    <maven.compiler.target>21</maven.compiler.target>
    <native-maven-plugin.version>0.10.4</native-maven-plugin.version>
    <main.class>com.arpitrathore.byo.App</main.class>
    <image.name>ar-wc</image.name>
  </properties>

  <profiles>
    <profile>
      <id>native</id>
      <build>
        <plugins>
          <plugin>
            <groupId>org.graalvm.buildtools</groupId>
            <artifactId>native-maven-plugin</artifactId>
            <version>${native-maven-plugin.version}</version>
            <extensions>true</extensions>
            <executions>
              <execution>
                <id>build-native</id>
                <phase>package</phase>
                <goals>
                  <goal>compile-no-fork</goal>
                </goals>
              </execution>
            </executions>
            <configuration>
              <mainClass>${main.class}</mainClass>
              <imageName>${image.name}</imageName>
              <agent>
                <enabled>true</enabled>
              </agent>
            </configuration>
          </plugin>
        </plugins>
      </build>
    </profile>
  </profiles>
</project>

Building and Running the Project

  1. Compile the Java Code:

     $ mvn clean package -Pnative
    
  2. Run the Program and compare it with linux wc:

     $ ./target/ar-wc pom.xml                                                                                                   main main
          49      51    1626 pom.xml
     $ wc pom.xml                                                                                                               main main
          49   51 1626 pom.xml
    

Source Code

Entire source code used in this blog can be found here: link


Conclusion

Building CLI tools with Java and GraalVM opens up a world of possibilities for efficient and portable applications. This wc tool is a simple example that demonstrates how you can leverage GraalVM to enhance Java’s capabilities. Try extending the tool with additional features, such as flag-based options (-l for lines, -w for words, etc.) or support for multiple files.

Let me know how you’ve used GraalVM for your projects!

0
Subscribe to my newsletter

Read articles from Arpit Rathore directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Arpit Rathore
Arpit Rathore

Senior Backend Engineer