MicrostarterCli is a command-line rapid development tool for Micronaut applications. It helps developers cut the development time and focus on the application logic by generating Micronaut components and configurations using ready-built templates. This tool is a Micronaut/PicoCLI application powered with ConsoleUI
- Technologies Stack
- Quick Start
- Init Command
- Configure Command
- Enum Command
- Entity Command
- Relationship Command
- Event Command
- Messaging Commands
- Security Command
- Metrics Command
- Banner Command
- Microstarter Domain Langauge
Type | Notes |
---|---|
Micronaut Application | default project type |
Functions | supporting AWS Lambda only |
Framework | Notes |
---|---|
Reactor | It is the recommended to use Reactor. |
Rxjava2 | It's not supported in security command. |
Rxjava3 | It's not supported in security command. |
Language | Notes |
---|---|
java | MicrostarterCli will detect the language from micronaut-cli.yml file |
groovy | MicrostarterCli will detect the language from micronaut-cli.yml file |
kotlin | MicrostarterCli will detecta the language from micronaut-cli.yml file |
Database | Type | Features | Notes |
---|---|---|---|
Mongodb | NoSQL | ReactiveMongo, GORM | GORM is in Preview stage and it is supported with Groovy Language Only.< |
H2 | RDBM | JPA , JDBC, R2DBC , GROM | 1. GORM is in Preview stage and it is supported with Groovy Language Only. 2. R2DBC is in Preview stage. |
MySQL | RDBM | JPA , JDBC , R2DBC , GROM | 1. GORM is in Preview stage and it is supported with Groovy Language Only. 2. R2DBC is in Preview stage . |
MariaDB | RDBM | JPA , JDBC, R2DBC , GROM | 1. GORM is in Preview stage and it is supported with Groovy Language Only. 2. R2DBC is in Preview stage . |
PostgreSQL | RDBM | JPA , JDBC, R2DBC , GROM | 1. GORM in Preview stage and it is supported with Groovy Language Only. 2. R2DBC is in Preview stage . |
Oracle | RDBM | JPA , JDBC, R2DBC , GROM | 1. GORM is in Preview stage and it is supported with Groovy Language Only. 2. R2DBC is in Preview stage . |
SqlServer | RDBM | JPA , JDBC, R2DBC , GROM | 1. GORM is in Preview stage and it is supported with Groovy Language Only. 2. R2DBC is in Preview stage . |
Microstream | Embedded File Storage | Microstream | implemented with Micronaut-Microstream annotations |
Framework/Tools | notes |
---|---|
Liquibase | recommended |
Flyway | It's not yet implemented in "security" command. |
Framework/Tools | Notes |
---|---|
graphql-java-tools |
Framework/Tools | Notes |
---|---|
Caffeine |
Framework/Tools | Notes |
---|---|
Micrometers | |
InfluxDB | |
prometheus |
Messaging | Notes |
---|---|
Kafka | |
RabbitMQ | |
NAT | |
GCP |
Mechanism | Notes |
---|---|
JWT | |
Sessions | |
Basics |
The MicrostarterCli uses Banana to generate banners.
As a getting started step, we will generate an application using The Micronaut Launch. Then, we will generate a Fruit entity, repository, service, REST API, and GraphQL endpoints.
- Download the MicrostarterCli.
- Generate a Micronaut Application using Micronaut Launch or Micronaut CLI.
- Unzip MicrostarterCli in the Micronaut Application or configure it in your environment.
- Open the Terminal/Command Prompt. And navigate to the project's directory.
- Run this command to start generating the Fruit entity
mc entity -e fruit --graphql
init Command: Enables you to generate Micronaut Applications from Micronaut Launch.
Example 1:
> mc init
Example 2:
> mc init --name FruitService --package io.hashimati
Please check the Micronaut Launch API: https://launch.micronaut.io/swagger/views/swagger-ui/index.html
You can also generate a project using the Microstarter Domain Langauge by passing a script file into the --file
parameter.
Example:
Copy the below script and save in a file with name "FruitService.hdl".
service FruitService{
port 8080;
reactive reactor;
package io.hashimati
build gradle;
database MicroStream;
language java;
dao jdbc;
migrationTool liquibase;
annotation micronaut;
tracing jaeger;
testFramework junit;
entity Fruit {
name:String;
quantity: int;
microstreamPath D:/fruitServiceMicrostream;
}
}
Then run the below command:
>mc init --file FruitService.hdl
The above command will download a Microanut project from Micronaut launch. Then, it will configure the project according to the specified configuration. Finally, it will generate the Fruit
entity.
> mc configure
The "configure" command prepares a Micronaut application to be used by MicrostarterCli's commands. The command should be run once. The first action that "configure" command does is reading "micronaut-cli.yml" file and collects the application's information. Based on the infomration in the "miconaut-cli.yml", MicrostarterCli application will check if the application type is supported by MicrostarterCli and determines generating Micronaut components flow.
The "Configure" command adds the necessary features and configurations that are required by other command to the Micronaut Application. When a user runs the command it will ask the user to configure the below:
- Reactive framework: Reactor Project, RxJava2 , RxJava3.
- Database Name.
- Database Type.
- Messaging Framework
- Caching
- Metrics Observibiltiy
- GraphQL
In the other hand, the "configure" does the below configurations by default:
- Adding the OpenAPI features and adding the necessary YAML and Java annotations configurations.
- Adding "OpenWriter" features.
- Updating the "logback.xml" file. It will add the "FILE" appender to the file, which will let the Miconaut application to write/append the logs to "logs.log" file. Also, it will add "io.micronaut.data.query" logger to trace the Micronaut data events.
- File Appender Configuration:
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>logs.log</file>
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
...
<root level="info">
<appender-ref ref="STDOUT" />
<appender-ref ref="FILE" />
</root>
"io.micronaut.data.query" Logger Configuration:
<logger name="io.micronaut.data.query" level="trace" />
- Adding Lombok to the Micornaut-java application if the Micronaut application doesn't contain Lombok.
- Creates "MicoCliConfig.json" file. The "MicroCliConfig.json" file contains the application informations from "micronaut-cli.yml" file and tarcks all the actions that users do using the MicrostarterCli tool.
The "configure" command runs implicitly if the user runs Entity Command.
> mc create-enum --name <Enum Name> --options <OPTION1,OPTION2,OPTION3, ...>
The users can use"enum" command declare and configure an Enum data type in the applicaiton. The defined enum data type will appear in the attributes data type selection list in Enum Command.
> mc create-enum --name WHETHER --options SUNNY,CLOUDY,RAINY
mc entity --entity-name <EntityName> --collection-name <collection name> --graphql --cache --no-endpoint
The "entity" command helps the developers to bootstarp the code of the basic CRUD operations for the application's domains. The files are including Entity Class file, Database Repository file, Service file, REST Endpoint file, and GraphQL configuration files. The "entity" command generates the files based on the information that the developer provides in "configure" command. The "entity" command runs the "configure" command implicitly if the developer didn't run it.
Option | Aliases | Description |
---|---|---|
--entity-name | -e , -n | To specify the entity's name |
--collection-name | -c | to specify the entity's table/collection name |
--no-endpoint | to generate the entity class only. | |
--graphql | -gl | to generate entity's graphql configuration and files including QueryFactory, QueryResolver, schema, data, query,and mutation files |
--cache | --caffine | to add caching annotations in the entity's service file |
> mc entity -n Fruit
@Data
@ToString
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode
@Builder
@Schema(name="Fruit", description="Fruit Description")
@MappedEntity(value = "fruits", namingStrategy = Raw.class)
public class Fruit{
@Id
@GeneratedValue(GeneratedValue.Type.AUTO)
@EqualsAndHashCode.Exclude
private Long id;
private String name;
@DateCreated
private Date dateCreated;
@DateUpdated
private Date dateUpdated;
}
@Repository
@JdbcRepository(dialect = Dialect.H2)
public interface FruitRepository extends CrudRepository<Fruit, Long> {
}
@Singleton
@Transactional
public class FruitService {
private static final Logger log = LoggerFactory.getLogger(FruitService.class);
@Inject private FruitRepository fruitRepository;
@Timed(value = "io.hashimati.services.fruitService.save", percentiles = { 0.5, 0.95, 0.99 }, description = "Observing all service metric for saving fruit object")
public Fruit save(Fruit fruit){
log.info("Saving Fruit : {}", fruit);
//TODO insert your logic here!
//saving Object
fruitRepository.save(fruit);
return fruit;
}
@Timed(value = "io.hashimati.services.fruitService.findById", percentiles = { 0.5, 0.95, 0.99 }, description = "Observing all service metric for finding a fruit object by id")
public Fruit findById(long id){
log.info("Finding Fruit By Id: {}", id);
return fruitRepository.findById(id).orElse(null);
}
@Timed(value = "io.hashimati.services.fruitService.deleteById", percentiles = { 0.5, 0.95, 0.99 }, description = "Observing all service metric for deleting a fruit object by id")
public boolean deleteById(long id){
log.info("Deleting Fruit by Id: {}", id);
try{
fruitRepository.deleteById(id);
log.info("Deleted Fruit by Id: {}", id);
return true;
}
catch(Exception ex)
{
log.info("Failed to delete Fruit by Id: {}", id);
ex.printStackTrace();
return false;
}
}
@Timed(value = "io.hashimati.services.fruitService.findAll", percentiles = { 0.5, 0.95, 0.99 }, description = "Observing all service metric for finding all fruit objects")
public Iterable<Fruit> findAll() {
log.info("Find All");
return fruitRepository.findAll();
}
public boolean existsById(Long id)
{
log.info("Check if id exists: {}", id);
return fruitRepository.existsById(id);
}
@Timed(value = "io.hashimati.services.fruitService.update", percentiles = { 0.5, 0.95, 0.99 }, description = "Observing all service metric for update a fruit object")
public Fruit update(Fruit fruit)
{
log.info("update {}", fruit);
return fruitRepository.update(fruit);
}
}
@Controller("/api/fruit")
public class FruitController {
private static final Logger log = LoggerFactory.getLogger(FruitController.class);
@Inject private FruitService fruitService;
@Post("/save")
@Version("1")
@Timed(value = "io.hashimati.controllers.fruitController.save", percentiles = { 0.5, 0.95, 0.99 }, description = "Observing all service metric for saving fruit object")
@Operation(summary = "Creating a fruit and Storing in the database",
description = "A REST service, which saves Fruit objects to the database.",
operationId = "SaveFruit"
)
@ApiResponse(
content = @Content(mediaType = "application/json")
)
@ApiResponse(responseCode = "400", description = "Invalid Object Supplied")
@ApiResponse(responseCode = "404", description = "Fruit not stored")
public Fruit save(@Body Fruit fruit){
log.info("Saving Fruit : {}", fruit);
//TODO insert your logic here!
//saving Object
return fruitService.save(fruit);
}
@Get("/get")
@Version("1")
@Timed(value = "io.hashimati.controllers.fruitController.findById", percentiles = { 0.5, 0.95, 0.99 }, description = "Observing all service metric for finding a fruit object by id")
@Operation(summary = "Getting a fruit by Id",
description = "A REST service, which retrieves a Fruit object by Id.",
operationId = "FindByIdFruit"
)
@ApiResponse(
content = @Content(mediaType = "application/json")
)
@ApiResponse(responseCode = "400", description = "Invalid Id Supplied")
@ApiResponse(responseCode = "404", description = "Fruit not found")
public Fruit findById(@Parameter("id") long id){
return fruitService.findById(id);
}
@Delete("/delete/{id}")
@Version("1")
@Timed(value = "io.hashimati.controllers.fruitController.deleteById", percentiles = { 0.5, 0.95, 0.99 }, description = "Observing all service metric for deleting a fruit object by id")
@Operation(summary = "Deleting a fruit by ID",
description = "A REST service, which deletes Fruit object from the database.",
operationId = "DeleteByIdFruit"
)
@ApiResponse(
content = @Content(mediaType = "boolean")
)
@ApiResponse(responseCode = "400", description = "Invalid Id Supplied")
@ApiResponse(responseCode = "404", description = "Fruit not found")
public boolean deleteById(@PathVariable("id") long id){
log.info("Deleting Fruit by Id: {}", id);
return fruitService.deleteById(id);
}
@Get("/findAll")
@Version("1")
@Timed(value = "io.hashimati.controllers.fruitController.findAll", percentiles = { 0.5, 0.95, 0.99 }, description = "Observing all service metric for finding all fruit objects")
@Operation(summary = "Retrieving all fruit objects as Json",
description = "A REST service, which returns all Fruit objects from the database.",
operationId = "FindAllFruit"
)
@ApiResponse(
content = @Content(mediaType = "application/json")
)
public Iterable<Fruit> findAll(){
log.info("find All");
return fruitService.findAll();
}
@Put("/update")
@Version("1")
@Timed(value = "io.hashimati.controllers.fruitController.update", percentiles = { 0.5, 0.95, 0.99 }, description = "Observing all service metric for update a fruit object")
@Operation(summary = "Updating a fruit.",
description = "A REST service, which update a Fruit objects to the database.",
operationId = "UpdateFruit"
)
@ApiResponse(
content = @Content(mediaType = "application/json")
)
@ApiResponse(responseCode = "404", description = "Fruit not found")
public Fruit update(@Body Fruit fruit)
{
log.info("update {}", fruit);
return fruitService.update(fruit);
}
}
@Client("/api/fruit")
public interface FruitClient {
@Post("/save")
public Fruit save(Fruit fruit);
@Get("/get")
public Fruit findById(@Parameter("id") long id);
@Delete("/delete/{id}")
public boolean deleteById(@PathVariable("id") long id);
@Get("/findAll")
public Iterable<Fruit> findAll();
@Put("/update")
public Fruit update(@Body Fruit fruit);
}
The developers can add relationship between two generated entities. The command asks the use the following:
- First entity.
- Second entity.
- The relationship type: One-to-One or One-to-Many.
> mc create-relation
"Event" Command enables you to generate Event's Publisher/Listener for an Entity.
> mc event
The developers can use Messaging commands to generated producer/consumer components for the entities generated using "entity" command. MicrostarterCli supports the following messaging systems:
- Kafka.
- RabbitMQ.
- Nats
- GCP-PubSub.
Each messaging system has two commands. The first command is for generating the Listener Component. The second command is for generating the Client Component. The messaging commands ask the developers to provide the following information:
- The class/interface's package.
- The class/interface's name.
- The GroupID if required by the system.
- The "Subject", "Topic", or "Queue" based on the messaging system.
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.micronaut.messaging.annotation.MessageBody;
import io.micronaut.nats.annotation.NatsListener;
import io.micronaut.nats.annotation.Subject;
import io.hashimati.domains.Fruit;
@NatsListener
public class FruitsListener {
private static final Logger log= LoggerFactory.getLogger(FruitsListener.class);
@Subject("fruits")
public void receive(@MessageBody Fruit message)
{
log.info("Received {}", message);
}
}
import io.micronaut.nats.annotation.NatsClient;
import io.micronaut.nats.annotation.Subject;
import io.micronaut.messaging.annotation.MessageBody;
import io.hashimati.domains.Fruit;
@NatsClient
public interface FruitClient {
@Subject("fruit")
public void send(Fruit message);
}
> create-kafka-listener <-e <entityName>>
kafka-listener - kafkaListener
Option | Alias | Mandatory | Description |
---|---|---|---|
-e | --entity | No | To pass the entity name. |
> create-kafka-client <-e <entityName>>
kafka-client - kafkaClient
Option | Alias | Mandatory | Description |
---|---|---|---|
-e | --entity | No | To pass the entity name. |
> create-rabbitmq-listener <-e <entityName>>
rabbitmqListener - rabbitmq-Listener
Option | Alias | Mandatory | Description |
---|---|---|---|
-e | --entity | No | To pass the entity name |
> create-rabbitmq-listener <-e <entityName>>
rabbitmq-client - rabbitmqClient
Option | Alias | Mandatory | Description |
---|---|---|---|
-e | --entity | No | To pass the entity name. |
> create-nat-listener <-e <entityName>>
nats-Listener - natsListener
Option | Alias | Mandatory | Description |
---|---|---|---|
-e | --entity | No | To pass the entity name. |
> create-nat-client <-e <entityName>>
nats-client - natsClient
Option | Alias | Mandatory | Description |
---|---|---|---|
-e | --entity | No | To pass the entity name. |
> create-gcp-pubsub-listener <-e <entityName>>
pubsub-listener - pubSubListener
Option | Alias | Mandatory | Description |
---|---|---|---|
-e | --entity | No | To pass the entity name. |
> create-gcp-pubsub-client <-e <entityName>>
pubsub-client - pubsubClient - PubSubClient
Option | Alias | Mandatory | Description |
---|---|---|---|
-e | --entity | No | To pass the entity name. |
MicrostarterCli helps to bootstrap the security authentication mechanisim using the "security" command. The MicrostarterCli supports the following Mechanisim:
- Basic.
- Session.
- JWT.
The "security" command requires the "configure" to be run first and it will boostrap the security files accordingly. Please, ensure to configure according to the supported technologies in the below table:
Mechanisim | MongoDB | JDBC | JPA | GORM | Liquibase | Flyway | Reactor | RxJava2 | RxJava3 |
---|---|---|---|---|---|---|---|---|---|
Basic | Yes | Yes | - | - | Yes | - | Yes | - | - |
Session | Yes | Yes | - | - | Yes | - | Yes | - | - |
JWT | Yes | Yes | - | - | Yes | - | Yes | - | - |
> mc security
The "metrics" command configurs the metircs registry in the micronaut application.
> mc configure-metrics
The banner command allows to the user to customize the displayed banner at launch time. Please refer to quick start section for the demo.
> mc banner
Microstarer Domain Language is Domain Specific Application that used to describe the Service aspects including:
- Configurations
- Enums
- Entities
- Security rules
- Relationship (Upcoming Feature)
- Microservices configurations (Upcoming Feature)
- Clients (Upcoming Feature)
.hdl is the conventional extensions for the Microstarter Domain Language.
The below is a simple exampla to define a Micronaut project using Microstarter Domain Language.
service FruitService{
// Project Configuration
port 8080;
reactive reactor;
build gradle;
database MicroStream;
language java;
package io.hashimati
dao jdbc;
migrationTool liquibase;
annotation micronaut;
tracing jaeger;
testFramework junit;
//an entity definition
entity Fruit {
name:String;
quantity: int;
microstreamPath D:/fruitServiceMicrostream;
}
}
As shown below, you can declare a service as follows:
service <serviceName> {
...
<Service Configuration Commands>;
...
<entities>
...
<Entity Relationships>
...
<Secuirty>
...
<Clients>
...
}
The keywords service begins the service definition and followed by the servcie name. the body service body is sorrounded by curly braces the service body could contain the following:
- Service Configuration commands.
- Entity Declarations.
- Entities' Relationships.
- Security Declaration.
- Client Declaration.
The service configuration is as follows:
command <parameters>;
The support service configuration commands are:
Command | Parameter | Values | Default Value |
---|---|---|---|
package | package name | example: io.demo | io.demo |
language | main source language | java, groovy, kotlin | java |
build | build tool name | gradle, maven | gradle |
testFramework | test framework name | junit, spock, kotest | language default |
database | database type name | mongodb, microstream, H2, mysql, postgres, oracle, sqlserver | H2 |
dao | data access types | Relational DB:[jdbc,r2dbc, jpa] Mongodb[data-mongodb,data-mongodb-reactive,mongo-reactive] | [jdbc, data-mongodb, microstream] |
migrationTool | Migration tool name | liquibase , Flyway | Liquibase |
reactive | reactive feature name | reactor, RxJava2, RxJava3 | reactor |
tracing | tracing tool Name | jaeger, zipkin | jaeger |
messaging | Messaging Tool Name | kafka, rabbitmq, nats | none |
graphql | no parameters | - | - |
annotation | framework name | micronaut, Jaxb | micronaut |
port | port number | 0-65535 | 8080 |
As show below, you can declare an entity as follows:
entity <entityName>{
...
<attribute declartion>;
...
<entity configuration commands>
...
}
The entity declaration starts with entity
keywords followed by the entity name
and the body surrounded by curly barces. In the entity body you can include the following:
- Attribute declarations.
- Entity configuration commands.
The attribute declaration starts with attribute name
followed by type
and validations.
<name>: <type> <validations>;
String, byte, char, short, int long, float, double, defined enums (to be implemented), and defined classes (to be implemented).
The syntax as follows:
command <parameters>;
Supported commands:
Command | Parameter | Values | Default Value |
---|---|---|---|
records | none | - | - |
graphql | none | - | - |
microstreamPath | datastore path | path | null |
Supported attribute validation
Validation |
---|
required |
unique |
notnull |
min(x) |
max(y) |
size(x,y) |
regex(your regex ) |
- Be a direct contributor.
- Share an idea that adds value to this project.
- Report an issue.
- Spread words about this project.
- Give a star to this project.
- Sponsor this project.