tests
# TaskManager
A lightweight, elegant task scheduler for Kotlin and Java with support for cron expressions, fixed-rate scheduling, and manual execution.
## Features
- ✅ **Simple API** - Minimal concepts, maximum clarity
- ✅ **Cron Support** - Full cron expression support via cron-utils
- ✅ **Fixed Rate & Delay** - Flexible scheduling options
- ✅ **Manual Execution** - Run tasks on-demand
- ✅ **Context Management** - Type-safe context injection
- ✅ **Lifecycle Hooks** - Task start/complete callbacks
- ✅ **Concurrency Control** - Allow or prevent concurrent executions
- ✅ **Java Friendly** - Builder pattern for Java users
- ✅ **Zero Dependencies** - Except cron-utils for cron parsing
## Quick Start
### Kotlin
```kotlin
val tasks = TaskManager {
concurrency = 4
context["db"] = database
}
// Heartbeat every 5 seconds
tasks.task("heartbeat", every = 5.seconds) {
println("heartbeat")
}
// Cron task - every hour
tasks.task("cleanup", cron = "0 0 * * * ?") {
val db = get<Database>("db")
db.cleanup()
}
// Manual task
tasks.task("rebuild-index") {
rebuildIndex()
}
tasks.start()
// Manually execute
tasks.run("rebuild-index")
tasks.shutdown()
```
### Java
```java
TaskManager tasks = TaskManager.builder()
.concurrency(4)
.putContext("db", database)
.build();
// Heartbeat every 5 seconds
tasks.task("heartbeat", null, Duration.ofSeconds(5), false, ctx -> {
System.out.println("heartbeat");
return null;
});
// Cron task
tasks.task("cleanup", "0 0 * * * ?", null, false, ctx -> {
Database db = ctx.get("db");
db.cleanup();
return null;
});
tasks.start();
tasks.shutdown(true);
```
## Installation
Add to your `build.gradle.kts`:
```kotlin
dependencies {
implementation("com.taskmanager:taskmanager:1.0.0")
}
```
## Core Concepts
### 1. Task Registration
Register tasks with simple or advanced scheduling:
```kotlin
// Simple - Fixed rate
tasks.task("task1", every = 10.seconds) { }
// Simple - Cron
tasks.task("task2", cron = "0 * * * * ?") { }
// Simple - Manual only
tasks.task("task3") { }
// Advanced - Schedule object
tasks.task("task4", Schedule.WithInitialDelay(
delay = 1.minutes,
schedule = Schedule.FixedRate(5.seconds)
)) { }
```
### 2. Context Management
Inject and use context values:
```kotlin
val tasks = TaskManager {
context["db"] = database
context["config"] = config
}
tasks.task("process") {
val db = get<Database>("db") // Throws if not found
val cache = getOrNull<Cache>("cache") // Returns null if not found
val timeout = getOrDefault("timeout", 30) // Returns default if not found
// Set task-local values
set("processed", 100)
}
```
### 3. Lifecycle Management
```kotlin
val tasks = TaskManager {
autoStart = false // Don't start immediately
onTaskStart = { event ->
println("Starting ${event.taskName}")
// Dynamically inject context
event.context["timestamp"] = System.currentTimeMillis()
}
onTaskComplete = { exec ->
if (exec.isSuccess) {
println("${exec.taskName} took ${exec.duration}ms")
} else {
println("${exec.taskName} failed: ${exec.error}")
}
}
}
tasks.start() // Start scheduling
tasks.shutdown(awaitTermination = true) // Graceful shutdown
```
### 4. Task Control
```kotlin
tasks.enable("task1") // Enable scheduling
tasks.disable("task1") // Disable scheduling (doesn't affect running tasks)
tasks.remove("task1") // Remove task completely
// Query
tasks.exists("task1") // Check existence
tasks.getTaskDefinition("task1") // Get task metadata
tasks.listTaskNames() // List all tasks
```
### 5. Manual Execution
```kotlin
// Async execution
val future = tasks.run("task1")
val result = future.get()
// Sync execution
val result = tasks.runBlocking("task1")
// With additional context
tasks.run("task1", mapOf("param" to "value"))
```
### 6. Concurrency Control
```kotlin
// Don't allow concurrent execution (default)
tasks.task("exclusive", every = 5.seconds, allowConcurrent = false) {
Thread.sleep(10000) // Even if this takes longer, next execution waits
}
// Allow concurrent execution
tasks.task("parallel", every = 5.seconds, allowConcurrent = true) {
Thread.sleep(10000) // Multiple instances can run simultaneously
}
```
## Advanced Examples
### Custom Monitoring
```kotlin
class TaskMonitor {
private val stats = ConcurrentHashMap<String, TaskStats>()
data class TaskStats(
var totalExecutions: Long = 0,
var successCount: Long = 0,
var failureCount: Long = 0,
var averageDuration: Long = 0
)
fun record(execution: TaskExecution) {
val stat = stats.getOrPut(execution.taskName) { TaskStats() }
stat.totalExecutions++
if (execution.isSuccess) stat.successCount++
// ... update stats
}
}
val monitor = TaskMonitor()
val tasks = TaskManager {
onTaskComplete = { exec -> monitor.record(exec) }
}
```
### Dynamic Context Injection
```kotlin
val tasks = TaskManager {
onTaskStart = { event ->
// Inject context based on task type
when {
event.taskName.startsWith("db-") -> {
event.context["connection"] = connectionPool.acquire()
}
event.taskName.startsWith("api-") -> {
event.context["token"] = refreshToken()
}
}
}
}
```
### Retry Logic
```kotlin
tasks.task("resilient-task") {
retry(times = 3, delay = 5.seconds) {
riskyOperation()
}
}
// Helper function
fun <T> retry(times: Int, delay: Duration, block: () -> T): T {
repeat(times - 1) {
try {
return block()
} catch (e: Exception) {
Thread.sleep(delay.toMillis())
}
}
return block() // Last attempt
}
```
## Schedule Types
```kotlin
// Cron expression (Quartz format)
Schedule.Cron("0 0 * * * ?") // Every hour
// Fixed rate (from start time)
Schedule.FixedRate(Duration.ofSeconds(10))
// Fixed delay (from completion time)
Schedule.FixedDelay(Duration.ofSeconds(10))
// One-time execution
Schedule.Once(Instant.now().plusSeconds(60))
// With initial delay
Schedule.WithInitialDelay(
delay = Duration.ofMinutes(1),
schedule = Schedule.FixedRate(Duration.ofSeconds(5))
)
```
## Cron Expression Format
Uses Quartz cron format:
```
┌───────────── second (0-59)
│ ┌───────────── minute (0-59)
│ │ ┌───────────── hour (0-23)
│ │ │ ┌───────────── day of month (1-31)
│ │ │ │ ┌───────────── month (1-12 or JAN-DEC)
│ │ │ │ │ ┌───────────── day of week (0-6 or SUN-SAT)
│ │ │ │ │ │
* * * * * *
```
Examples:
- `0 0 * * * ?` - Every hour at minute 0
- `0 */5 * * * ?` - Every 5 minutes
- `0 0 9-17 * * MON-FRI` - Every hour from 9am to 5pm, Monday to Friday
- `0 0 0 1 * ?` - First day of every month at midnight
## Configuration Options
```kotlin
TaskManagerConfig {
// Number of threads in pool
var concurrency: Int = Runtime.getRuntime().availableProcessors()
// Thread name prefix
var threadNamePrefix: String = "task-manager"
// Auto-start scheduling on creation
var autoStart: Boolean = false
// Global context
val context: MutableMap<String, Any>
// Lifecycle hooks
var onTaskStart: ((TaskStartEvent) -> Unit)? = null
var onTaskComplete: ((TaskExecution) -> Unit)? = null
}
```
## Best Practices
1. **Use context for shared resources**: Database connections, configurations, etc.
2. **Implement monitoring via hooks**: Don't let the framework dictate your monitoring strategy
3. **Handle errors in tasks**: Wrap risky operations in try-catch
4. **Use allowConcurrent wisely**: Most tasks should not allow concurrent execution
5. **Graceful shutdown**: Always call `shutdown(awaitTermination = true)`
6. **Test cron expressions**: Use online tools to validate before deployment
## Thread Safety
- All public methods are thread-safe
- Context maps use `ConcurrentHashMap`
- Task execution is isolated
- Safe to call from multiple threads
## License
MIT License
## Credits
Built with:
- [cron-utils](https://github.com/jmrozanec/cron-utils) - Cron expression parsing