JStatic Docs Javadocs

Setup JStatic as a Gradle task

In this guide you will learn how to set up the JStatic as a Gradle task. We will then take it to a more in-depth guide on how to use JStatic to generate Java source code from OpenAPI spec as a concrete example.

Simple Gradle Task

Setting up JStatic as a Gradle task is pretty simple and straight forward:

configurations {
  jstaticRuntime
}

dependencies {
  // TODO replace with desired version
  jstaticRuntime 'com.mohamnag:jstatic:X.Y.Z'
}

task jstaticRuntime(type: JavaExec) {
  group = "Build"
  classpath = configurations.jstaticRuntime
  main = 'com.mohamnag.jstatic.Application'
  // TODO replace with the path to your config.yaml file
  args "--config=jstatic-config.yaml"
}

Above snippet:

Generate Source Files from OpenAPI

While you might potentially use JStatic as a static HTML content generator integrated in your Gradle build process, there are more exciting use-cases possible for it. We will show you here how to use it to generate Java POJOs out of an OpenAPI specification file fully customizable to your needs.

To keep things separate, we will create a dedicated directory for our code generation and will call it codegen.

Setting up Gradle task

As first step we add a codegen.gradle file to that directory with following content:

// FILE: codegen/codegen.gradle
configurations {
  openApiCodegen
}

dependencies {
  // TODO replace with desired version
  openApiCodegen 'com.mohamnag:jstatic:X.Y.Z'
}

sourceSets {
  // TODO replace with output path that you will setup later in your config.yaml file
  main.java.srcDirs += "${buildDir}/generated/api/src/main/java/"
}

task openApiCodegen(type: JavaExec) {
  group = "Build"
  classpath = configurations.openApiCodegen
  main = 'com.mohamnag.jstatic.Application'
  // TODO replace with the path to your config.yaml file
  args "--config=codegen/config.yaml"
}

compileJava.dependsOn openApiCodegen

This script does many things similar to what we had in the simple task setup above but in addition:

Also pay attention that all paths in this file are still relative to the project root.

Next we will add following line to somewhere in the root build.gradle file:

// other stuff in project's main gradle file

apply from: 'codegen/codegen.gradle'

// other stuff in project's main gradle file

This makes sure that the partial snippet from above is applied to the main Gradle flow.

Setting up configuration

Now we will configure the JStatic. Add a config.yaml file to inside codegen directory with following content:

plugins:
  LOAD_DATA_TREE:
    - com.mohamnag.jstatic.plugins.front_matter_loader.MarkdownFrontMatterLoaderTask
    - com.mohamnag.jstatic.plugins.env_var_loader.EnvVariablesLoader
    - com.mohamnag.jstatic.plugins.openapi_processor.OpenApiProcessor

  PROCESS_ASSETS:

  COMPILE_TEMPLATES:
    - com.mohamnag.jstatic.plugins.handlebars_template_compiler.HandlebarsTemplateCompilerTask

  DUMP_TREE:
    - com.mohamnag.jstatic.plugins.html_file_dumper.FileDumperTask

front-matter-loader:
  data-dir: codegen/supplementary/

yaml-loader:
  data-dir: src/main/resources/

handlebars-template-compiler:
  base-dir: codegen/templates/
  default-template: default
  target-data-field: compiledHandlebars
  template-data-field: template
  start-node:
    - com
    - example
    - project
  helpers:
    - com.mohamnag.jstatic.plugins.handlebars_template_compiler.helpers.UrlHelpers
    - com.mohamnag.jstatic.plugins.handlebars_template_compiler.helpers.StringHelpers
    - codegen/templates/helpers.js

file-dumper:
  output-dir: build/generated/api/src/main/java/
  data-field: compiledHandlebars
  output-file-extension: .java

open-api-processor:
  component-template-parameters: openapi/parameter
  component-template-request-bodies: openapi/request_body
  component-template-responses: openapi/response
  component-template-schemas: openapi/schema

  component-package-parameters: com.example.project.dtos
  component-package-request-bodies: com.example.project.dtos
  component-package-responses: com.example.project.dtos
  component-package-schemas: com.example.project.dtos.schemas

This config:

To know more about details of what and how OpenApiProcessor works, take a look at its documentation here.

Adding templates

We defined following templates to be used in our configuration:

They will all be located under codegen/templates/ directory relative to the root of the project.

// default.hbs

{{page.data.body}}

/*
!! page.data

{{#each page.data}}
  {{@key}}: {{this}}
{{/each}}

!! this

{{#each this}}
  {{@key}}: {{this}}
{{/each}}
*/
// openapi/parameter.hbs

{{>default}}
// openapi/request_body.hbs

{{>default}}
// openapi/response.hbs

{{>default}}
// openapi/schema.hbs

{{#with page.data.component as |component|}}
  {{#if (eq component.type 'object')}}
{{> openapi/pojo
        className=page.name
        component=component
}}
  {{else}}
    {{#if (eq component.type 'array')}}
// arrays on root of schema not supported, yet!
    {{else}}
      {{#if (and component.enumValues (eq component.type 'string'))}}
{{> openapi/enum
        className=page.name
        component=component
}}
      {{else}}
        {{#if component.type}}
{{> openapi/valueObject
        className=page.name
        component=component
}}
        {{/if}}
      {{/if}}
    {{/if}}
  {{/if}}
  {{#unless component.type}}
// you forgot to set type on a component!
  {{/unless}}
{{/with}}

We will not dive deeper here as this is already getting too long, however you can see that above templates mostly use the default template which will just dump the important data under page and this so that you can build up on these.

The last template above, schema.hbs, is an example of how you can examine and differ to other templates based on type of component that you have got.

The last template we want to show here is enum.hbs:

package {{component.package}};

public enum {{name}} {
{{#each component.enumValues}}
  {{this}},
  {{/each}}
  ;
}

This is a very simple example of how to build a valid Java file (an enum class) out of values parsed from OpenAPI.

Summary

Here, we set up the base for generating Java code from OpenAPI spec files. Templates that we used need much more tuning, and you will need more of those templates to be able to generate a functional example. The good thing is that it is very flexible what you want to generate and how you want to do it. If you only need parts of it, you can omit the rest. If you want to rename things or bend them a bit, it's all yours to decide.