JeKa : An Alternative to Maven and Gradle

Jérôme Angibaud
Javarevisited
Published in
6 min readJul 19, 2023

Over the past decade, Java ecosystem has been dominated by two prominent build tools : Maven and Gradle. While, they share advantages that are inherent to their popularity : large user base, documentation, support, and wide range of plugins, they face some recurring criticisms. Maven is often criticized for being rigid and verbose, while Gradle is sometimes considered heavy, complex and obfuscating.

If you find yourself feeling frustrated while using Maven and Gradle, you might want to consider giving JeKa a try. JeKa is a lightweight build tool that offers a unique set of features and principles:

  • Java-centric approach: JeKa is primarily designed to be configured and executed using pure Java code, although Kotlin is also supported as an alternative.
  • Easy execution and debugging: Builds can be effortlessly run and debugged within the IDE, just like any regular Java application.
  • Simplicity, transparency, and flexibility: Users should have the ability to easily navigate through the codebase, discover features, and troubleshoot issues.
  • Conciseness : In standard cases, no build code should be required. However, if build code becomes necessary, it should be kept concise.
  • Integration with third party tool : JeKa can integrate seamlessly with any tool providing a Java library or a command line interface.

As an example, consider this build class that can construct a Java project and publish it to a repository. While JeKa offers a more concise approach, this explicit example demonstrates the process in a clear manner.


// We import here, a library for displaying ascii art.
// This library is exclusively utilized within the build code.
@JkInjectClasspath("com.github.lalyos:jfiglet:0.0.9")
class Build extends JkBean {

@JkDoc("If true, a ascii art is dsplayed when build succeed")
public boolean displayAscii = true;

// Contains all info and methods to build the actual project
final JkProject project;

// Set of repo (actual a single one) to publish the jar.
final JkRepoSet publishRepos;

private Build() {

// Get publish repo configured globally
publishRepos = JkRepoProperties.of(this.getRuntime().getProperties())
.getPublishRepository());

// Configure the project to build
project = JkProject.of().flatFacade()
.setJvmTargetVersion(JkJavaVersion.V17)
.configureCompileDependencies(deps -> deps
.and("com.google.guava:guava:21.0")
.and("org.threeten:threetenbp:1.6.8")
.and("org.projectlombok:lombok:1.18.28")
)
.configureRuntimeDependencies(deps -> deps
.minus("org.projectlombok:lombok")
)
.configureTestDependencies(deps -> deps
.and("org.junit.jupiter:junit-jupiter:5.9.2")
.and("org.mockito:mockito-core:5.4.0")
)
.setPublishedModuleId("org.example:jeka-sample")
.setPublishedVersionFromGitTag(); // Version inferred from Git
project.publication.setRepos(publishRepos);
}

@JkDoc("Clean, compile, test and package to a jar archive")
public void cleanPack() throws IOException {
cleanOutput(); project.pack(); // compile, test and make jar

// display ascii art banner, on success
if (displayAscii) {
System.out.println(FigletFont.convertOneLine("Success"));
}
}

@JkDoc("Pulish the built project to Maven repository")
public void publish() {
project.publication.publish();
}

}

Using this class, we can run or debug any public methods from the IDE leveraging IntelliJ Plugin or by adding a main method.

We can also invoke it directly from command line, for instance, using command ./jekaw #cleanPack #publish #asciiArt=false. JeKa will handle the compilation of the Build class and resolve its dependencies automatically for you.

The only requirements are that the build class must inherit from JkBean and the build methods should be public, parameterless, and return void.

The @JkDoc annotation enables help messages during ./jekaw #help.

KBeans

KBeans are the central concept of JeKa, where every build class or pluggable component is represented as a KBean. The design of KBeans aims to be simple and straightforward.

JeKa incorporates bundled KBeans that facilitate interaction between various components. For instance, JeKa includes a KBean specifically designed for project building, eliminating the need to redefine build methods and defining defining default settings.

class Build extends JkBean {

final ProjectJkBean projectBean = getBean(ProjectJkBean.class)
.lately(this::configure);

private void configure(JkProject project) {
project.flatFacade()
.configureCompileDependencies(deps -> deps
.and("com.google.guava:guava:21.0")
.and("org.threeten:threetenbp:1.6.8")
.and("org.projectlombok:lombok:1.18.28")
)
.configureRuntimeDependencies(deps -> deps
.minus("org.projectlombok:lombok")
)
.configureTestDependencies(deps -> deps
.and("org.junit.jupiter:junit-jupiter:5.9.2")
.and("org.mockito:mockito-core:5.4.0")
)
.setPublishedModuleId("org.example:jeka-sample")
.setPublishedVersionFromGitTag();
}
}

The ProjectJkBean instance, also known as the project KBean, offers a preconfigured JkProject object that can be further customized using the configure method. This KBean provides various methods that can be invoked, such as ./jekaw project#cleanPack project#tests.skip=true.

To obtain a list of available methods and fields provided by a KBean, simply execute ./jeka project#help. This command will display the available options and assist in utilizing the functionalities offered by the KBean.

A KBean’s name, such as project, is automatically inferred from its class name, like ProjectJkBean. The KBean name is derived by converting the class name to lowercase and removing the “JkBean” suffix if present.

JeKa offers additional external KBeans for various functionalities such as Spring Boot, JaCoCo, SonarQube, Node.js, and more. These KBeans can be effortlessly combined together as needed. It’s important to note that within the context of a project, a KBean always functions as a singleton.

@JkInjectClasspath("dev.jeka:springboot-plugin")
@JkInjectClasspath("dev.jeka:jacoco-plugin")
@JkInjectClasspath("dev.jeka:sonarqube-plugin")
class Build extends JkBean {

SpringbootJkBean springbootBean = getBean(SpringbootJkBean.class);

SonarqubeJkBean sonarqubeBean = getBean(SonarqubeJkBean.class);

Build() {

// Springboot will instantiate a project KBean and configure it
// to use proper dependencies versions and bootable jar packaging
springbootBean.setSpringbootVersion("3.1.1");

// Instantiate Jacoco KBean. This automatically configures
// project KBean instance to instrument test coverage
getBean(JacocoJkBean.class);

// The project is customised with the #configure method
springbootBean.projectBean.lately(this::configure);

// Customize Sonarqube. For project relatice configuration,
// SonarQube KBean will reuse info from project KBean.
sonarqubeBean.lately(sonarqube -> {
sonarqube.setProperty("sonar.host.url", "https://my.sonar.host:9000");
};
}

private void configure(JkProject project) {
project.flatFacade()
.configureCompileDependencies(deps -> deps
.and("org.springframework.boot:spring-boot-starter-web")
.configureTestDependencies(deps -> deps
.and("org.springframework.boot:spring-boot-starter-test")
);
}

}

To initiate a complete build, which includes test coverage and SonarQube analysis, simply execute “./jekaw project#cleanPack sonarQube#run”.

To explore the inner workings of JeKa, including the capabilities and options provided by KBeans and other objects, you can easily navigate within the JeKa code using an integrated development environment (IDE).

Use configuration instead of code

JeKa also provides the option to use a properties file instead of writing code for configuration. However, it’s worth noting that both approaches can be utilized together within the same project. In the case of configuring a Spring Boot project with test coverage and SonarQube analysis, the following local.properties file would suffice:

## import and instantiate the Springboot KBean
jeka.cmd._append.0=@dev.jeka:springboot-plugin springboot#

## import Jacoco and Sonarqube plugin
jeka.cmd._append.1=@dev.jeka:jacoco-plugin @dev.jeka:sonarqube-plugin

## define :build and :build_quality command shortcuts
jeka.cmd.build=project#cleanPack
jeka.cmd.build_quality=:build sonarqube#run jacoco# sonarqube#logOutput=true

## define the Java version for both compile/run build class and build project
jeka.java.version=17

## inject values to the Springboot KBean
springboot#springbootVersion=3.1.1

## Customize project source file locations (here /src and /test)
project#layout.style=SIMPLE

## Free properties. Sonarqube KBean will take in account those
## having 'sonar.' as prefix.
sonar.host.url=http://localhost:9000

By utilizing command shortcuts, executing ./jekaw :build_quality is sufficient to trigger a complete build that includes quality checks and test coverage.

The jeka.cmd._append.x properties allows for the appending of text to the command line. JeKa’s command line enables the injection of dependencies using the “@” symbol. In this scenario, there is no need to explicitly mention the version of the dependency, as it defaults to the same version as the current JeKa version.

Also, project dependencies can be conveniently defined in a dedicated file named project-dependencies.txt.

==== COMPILE ====
org.springframework.boot:spring-boot-starter-web
org.projectlombok:lombok:1.18.24

==== RUNTIME ====
- org.projectlombok:lombok
org.postgresql:postgresql:42.3.7

==== TEST ====
org.springframework.boot:spring-boot-starter-test

It’s worth noting that runtime dependencies are derived by complementing the compile dependencies. If there is a specific compile dependency that is not required during runtime, it can be excluded using the “-” symbol.

IntelliJ Plugin provides completion on both local.properties and project-dependencies.txt file for better experience.

To learn more about JeKa and explore its functionalities, you can refer to the following resources:

The simplest way to use JeKa, is to install the IntelliJ Plugin, as it contains everything you’ll need.

Your feedback is highly appreciated. Please do not hesitate to share your thoughts on what you liked, areas that can be improved, or any missing features you would like to see implemented. Your input is valuable in order to enhance the tool and provide a better experience.

--

--