This guide explains how to build command-line interface (CLI) applications using the tinystruct framework.
tinystruct's CLI capabilities are powered by the same action mechanism used for web applications, making it easy to create both CLI tools and web interfaces with shared code.
Unlike many other frameworks that treat CLI as an afterthought, tinystruct treats CLI and Web as equal citizens. This means:
- You can use the same
@Actionannotations for both modes. - Business logic is naturally shared without extra layers.
- You can test your web actions via CLI easily.
- It's perfect for AI-driven tasks and automation.
One of the modern features of tinystruct is that it doesn't require a main() method in your application classes. Applications are started directly using the bin/dispatcher tool. This reduces boilerplate and focuses your code on the logic itself.
@Action(value = "hello",
description = "Display a greeting",
mode = Action.Mode.CLI)
public String hello() {
return "Hello, World!";
}@Action(value = "hello",
description = "Display a greeting to the specified name",
mode = Action.Mode.CLI)
public String hello(String name) {
return "Hello, " + name + "!";
}@Action(value = "greet",
description = "Greet someone",
mode = Action.Mode.CLI)
public String greet() {
String name = getContext().getAttribute("--name");
String greeting = getContext().getAttribute("--greeting", "Hello");
if (name == null) {
return "Please provide a name with --name";
}
return greeting + ", " + name + "!";
}You can define command options using the options parameter in the @Action annotation:
@Action(
value = "generate",
description = "Generate code from a template",
options = {
@Argument(name = "template", alias = "t", description = "Template name"),
@Argument(name = "output", alias = "o", description = "Output directory")
},
mode = Action.Mode.CLI
)
public String generate() {
String template = getContext().getAttribute("--template");
String output = getContext().getAttribute("--output", "./output");
// Generate code using the template
return "Generated code using " + template + " template in " + output;
}# Basic command
bin/dispatcher hello --import com.example.MyApp
# Command with parameter
bin/dispatcher hello/John --import com.example.MyApp
# Command with named arguments
bin/dispatcher greet --name John --greeting Hi --import com.example.MyApp
# Command with options
bin/dispatcher generate --template entity --output ./src/main/java --import com.example.MyApp@Action(value = "interactive",
description = "Interactive command example",
mode = Action.Mode.CLI)
public void interactive() {
System.out.print("Enter your name: ");
Scanner scanner = new Scanner(System.in);
String name = scanner.nextLine();
System.out.print("Enter your age: ");
int age = scanner.nextInt();
System.out.println("Hello, " + name + "! You are " + age + " years old.");
}@Action(value = "process",
description = "Process with progress indicator",
mode = Action.Mode.CLI)
public void process() {
int total = 100;
for (int i = 0; i <= total; i++) {
System.out.print("\rProcessing: " + i + "% [");
int progress = i / 2;
for (int j = 0; j < 50; j++) {
if (j < progress) {
System.out.print("=");
} else if (j == progress) {
System.out.print(">");
} else {
System.out.print(" ");
}
}
System.out.print("] " + i + "%");
try {
Thread.sleep(50);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
System.out.println("\nProcessing complete!");
}@Action(value = "copy",
description = "Copy a file",
mode = Action.Mode.CLI)
public String copy() {
String source = getContext().getAttribute("--source");
String destination = getContext().getAttribute("--destination");
if (source == null || destination == null) {
return "Please provide --source and --destination parameters";
}
try {
Path sourcePath = Paths.get(source);
Path destinationPath = Paths.get(destination);
Files.copy(sourcePath, destinationPath, StandardCopyOption.REPLACE_EXISTING);
return "File copied successfully from " + source + " to " + destination;
} catch (IOException e) {
return "Error copying file: " + e.getMessage();
}
}@Action(value = "export-users",
description = "Export users to CSV",
mode = Action.Mode.CLI)
public String exportUsers() {
String outputFile = getContext().getAttribute("--output", "users.csv");
try {
Repository repository = Type.MySQL.createRepository();
repository.connect(getConfiguration());
List<Row> users = repository.query("SELECT id, name, email FROM users");
try (FileWriter writer = new FileWriter(outputFile);
CSVWriter csvWriter = new CSVWriter(writer)) {
// Write header
csvWriter.writeNext(new String[]{"ID", "Name", "Email"});
// Write data
for (Row user : users) {
csvWriter.writeNext(new String[]{
user.getString("id"),
user.getString("name"),
user.getString("email")
});
}
}
return "Exported " + users.size() + " users to " + outputFile;
} catch (Exception e) {
return "Error exporting users: " + e.getMessage();
}
}Tinystruct includes a built-in generate command that creates POJO classes and XML mapping files directly from your database schema. It automatically detects Java types from column definitions and adds the required import statements — no manual import specification is needed.
# Interactive mode — prompts for table names and output path
bin/dispatcher generate
# Non-interactive — specify tables directly
bin/dispatcher generate --tables users
# Multiple tables (semicolon-delimited)
bin/dispatcher generate --tables "users;orders;products"The generator:
- Supports MySQL, MSSQL, SQLite, and H2 databases
- Automatically imports
java.time.LocalDateTime,java.util.Date,java.sql.Timestamp, andjava.sql.Timebased on column types - Generates both the Java POJO and the XML mapping file
- Auto-detects the project's base package from your source tree
- Singularizes table names for class names (e.g.
users→User)
For full details and generated code examples, see the Database Integration — Built-in POJO Generator section.
-
Provide Clear Help: Always include descriptive help text for your commands.
-
Validate Input: Check command arguments and provide helpful error messages.
-
Progress Feedback: For long-running operations, provide progress indicators.
-
Exit Codes: Return appropriate exit codes to indicate success or failure.
-
Logging: Implement proper logging for debugging and auditing.
- Learn about Configuration
- Explore Database Integration
- Check out Advanced Features