Initial commit

This commit is contained in:
tchiotludo
2019-08-13 21:48:59 +02:00
commit d57e30c0c0
58 changed files with 2124 additions and 0 deletions

17
.gitignore vendored Normal file
View File

@@ -0,0 +1,17 @@
Thumbs.db
.DS_Store
.gradle
build/
target/
out/
.idea
*.iml
*.ipr
*.iws
.project
.settings
.classpath
.attach*
docker-compose.override.yml
cli/src/main/resources/application-override.yml

139
build.gradle Normal file
View File

@@ -0,0 +1,139 @@
buildscript {
ext {
micronautVersion = "1.2.0"
confluentVersion = "5.2.1"
kafkaVersion = "2.3.0"
avroVersion = "1.9.0"
}
}
plugins {
// micronaut
id "io.spring.dependency-management" version "1.0.6.RELEASE"
id "java"
id "net.ltgt.apt-eclipse" version "0.21"
id "net.ltgt.apt-idea" version "0.21"
id "com.github.johnrengelman.shadow" version "4.0.2"
id "application"
}
idea {
module {
downloadJavadoc = true
downloadSources = true
}
}
/**********************************************************************************************************************\
* Main
**********************************************************************************************************************/
mainClassName = "org.floworc.cli.App"
sourceCompatibility = 11
dependencies {
compile project(":cli")
}
/**********************************************************************************************************************\
* All projects
**********************************************************************************************************************/
allprojects {
group "org.floworc"
version "0.1"
repositories {
mavenCentral()
maven { url "https://jcenter.bintray.com" }
maven { url "http://packages.confluent.io/maven/" }
}
// micronaut
apply plugin:"java"
apply plugin:"net.ltgt.apt-eclipse"
apply plugin:"net.ltgt.apt-idea"
apply plugin: "io.spring.dependency-management"
dependencyManagement {
imports {
mavenBom "io.micronaut:micronaut-bom:" + micronautVersion
}
}
configurations {
// for dependencies that are needed for development only
developmentOnly
}
// dependencies
dependencies {
// utils
runtime "ch.qos.logback:logback-classic:1.2.3"
compile group: 'com.google.guava', name: 'guava', version: '27.1-jre'
compileOnly 'org.projectlombok:lombok:1.18.8'
annotationProcessor "org.projectlombok:lombok:1.18.8"
// micronaut
annotationProcessor "io.micronaut:micronaut-inject-java"
annotationProcessor "io.micronaut:micronaut-validation"
compile "io.micronaut:micronaut-runtime"
compile "io.micronaut:micronaut-inject"
compile "io.micronaut:micronaut-validation"
compile "info.picocli:picocli"
compile "io.micronaut.configuration:micronaut-picocli"
// test
testCompile "org.junit.jupiter:junit-jupiter-api"
testRuntime "org.junit.jupiter:junit-jupiter-engine"
testRuntime "org.junit.jupiter:junit-jupiter-params"
testAnnotationProcessor "io.micronaut:micronaut-inject-java"
testCompile "io.micronaut.test:micronaut-test-junit5"
testCompile "io.micronaut:micronaut-inject-java"
testImplementation 'org.hamcrest:hamcrest:2.1'
testImplementation 'org.hamcrest:hamcrest-library:2.1'
// floworc
compile group: 'com.devskiller.friendly-id', name: 'friendly-id', version: '1.1.0'
}
}
/**********************************************************************************************************************\
* Micronaut
**********************************************************************************************************************/
tasks.withType(JavaCompile){
options.encoding = "UTF-8"
options.compilerArgs.add("-parameters")
}
run.classpath += configurations.developmentOnly
test.classpath += configurations.developmentOnly
run.jvmArgs(
"-noverify",
"-XX:TieredStopAtLevel=1",
"-Dcom.sun.management.jmxremote",
'-Dmicronaut.environments=dev,override'
)
/**********************************************************************************************************************\
* Test
**********************************************************************************************************************/
test {
useJUnitPlatform()
testLogging {
exceptionFormat = "full"
}
}
/**********************************************************************************************************************\
* Jar
**********************************************************************************************************************/
jar {
manifest {
attributes "Main-Class": mainClassName
}
}
shadowJar {
mergeServiceFiles()
}

11
cli/build.gradle Normal file
View File

@@ -0,0 +1,11 @@
sourceCompatibility = 11
dependencies {
// micronaut
compile "io.micronaut:micronaut-management"
compile "io.micronaut:micronaut-http-client"
compile "io.micronaut:micronaut-http-server-netty"
// modules
compile project(":core")
}

View File

@@ -0,0 +1,26 @@
package org.floworc.core;
import io.micronaut.configuration.picocli.PicocliRunner;
import picocli.CommandLine;
import java.util.concurrent.Callable;
@CommandLine.Command(
name = "floworc",
version = "v0.1",
header = "floworc client",
mixinStandardHelpOptions = true,
subcommands = {
}
)
public class App implements Callable<Object> {
public static void main(String[] args) throws Exception {
PicocliRunner.call(App.class, args);
}
@Override
public Object call() throws Exception {
return PicocliRunner.call(App.class, "--help");
}
}

View File

@@ -0,0 +1,3 @@
micronaut:
application:
name: floworc

View File

@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">
<!-- Remove logback startup log -->
<statusListener class="ch.qos.logback.core.status.NopStatusListener" />
<property name="pattern" value="%d{ISO8601} %highlight(%-5.5level) %magenta(%-12.12thread) %cyan(%-12.12logger{12}) %msg%n" />
<withJansi>true</withJansi>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<target>System.out</target>
<immediateFlush>true</immediateFlush>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>DENY</onMatch>
</filter>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>WARN</level>
<onMatch>DENY</onMatch>
</filter>
<encoder>
<pattern>${pattern}</pattern>
</encoder>
</appender>
<appender name="STDERR" class="ch.qos.logback.core.ConsoleAppender">
<target>System.err</target>
<immediateFlush>true</immediateFlush>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>WARN</level>
</filter>
<encoder>
<pattern>${pattern}</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="STDOUT" />
<appender-ref ref="STDERR" />
</root>
<logger name="org.apache" level="WARN" />
<logger name="io.confluent" level="WARN" />
<!-- The configuration '%s' was supplied but isn't a known config. > https://github.com/apache/kafka/pull/5876 -->
<logger name="org.apache.kafka.clients.producer.ProducerConfig" level="ERROR" />
<logger name="org.apache.kafka.clients.admin.AdminClientConfig" level="ERROR" />
<logger name="org.apache.kafka.clients.consumer.ConsumerConfig" level="ERROR" />
</configuration>

13
core/build.gradle Normal file
View File

@@ -0,0 +1,13 @@
sourceCompatibility = 11
dependencies {
// serializers
compile group: "org.apache.avro", name: "avro", version: avroVersion
// yaml
compile group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.9.9.2'
compile group: 'com.fasterxml.jackson.dataformat', name: 'jackson-dataformat-yaml', version: '2.9.9'
// validations
compile group: 'org.hibernate.validator', name: 'hibernate-validator', version: '6.0.17.Final'
}

View File

@@ -0,0 +1,15 @@
package org.floworc.core.executions;
import lombok.Data;
import org.floworc.core.flows.State;
import java.util.Map;
@Data
public class Context {
private Map<String, Object> inputs;
private Map<String, Object> outputs;
private State state;
}

View File

@@ -0,0 +1,72 @@
package org.floworc.core.executions;
import lombok.*;
import lombok.experimental.Wither;
import org.floworc.core.flows.State;
import javax.validation.constraints.NotNull;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
@Value
@Builder
public class Execution {
@NotNull
private String id;
@NotNull
private String flowId;
@Wither
private List<TaskRun> taskRunList;
@Wither
private Context context;
@NotNull
private State state;
public Execution withState(State.Type state) {
return new Execution(
this.id,
this.flowId,
this.taskRunList,
this.context,
this.state.withState(state)
);
}
public TaskRun findTaskRunById(String id) {
Optional<TaskRun> find = this.taskRunList
.stream()
.filter(task -> task.getId().equals(id))
.findFirst();
if (!find.isPresent()) {
throw new IllegalArgumentException("Can't find taskrun with id '" + id + "' on execution '" + this.id + "'");
}
return find.get();
}
public Execution withTaskRun(TaskRun taskRun) {
ArrayList<TaskRun> newTaskRunList = new ArrayList<>(this.taskRunList);
//noinspection ResultOfMethodCallIgnored
Collections.replaceAll(
newTaskRunList,
this.findTaskRunById(taskRun.getId()),
taskRun
);
return new Execution(
this.id,
this.flowId,
newTaskRunList,
this.context,
this.state
);
}
}

View File

@@ -0,0 +1,89 @@
package org.floworc.core.executions;
import lombok.extern.slf4j.Slf4j;
import org.floworc.core.flows.State;
import org.floworc.core.tasks.Task;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
@Slf4j
public class ExecutionService {
public static Optional<List<TaskRun>> getNexts(Execution execution, List<Task> tasks) {
if (tasks.size() == 0) {
throw new IllegalStateException("Invalid execution " + execution.getId() + " on flow " +
execution.getFlowId() + " with 0 task"
);
}
// first one
if (execution.getTaskRunList() == null) {
return Optional.of(tasks.get(0).toTaskRun(execution));
}
// all done
long terminatedCount = execution
.getTaskRunList()
.stream()
.filter(taskRun -> taskRun.getState().isTerninated())
.count();
if (terminatedCount == tasks.size()) {
return Optional.empty();
}
// find first running
Optional<TaskRun> firstRunning = execution
.getTaskRunList()
.stream()
.filter(taskRun -> taskRun.getState().isRunning())
.findFirst();
if (firstRunning.isPresent()) {
return tasks
.get(execution.getTaskRunList().indexOf(firstRunning.get()))
.getChildTaskRun(execution);
}
// reverse
ArrayList<TaskRun> reverse = new ArrayList<>(execution.getTaskRunList());
Collections.reverse(reverse);
// find last created
Optional<TaskRun> lastCreated = reverse
.stream()
.filter(taskRun -> taskRun.getState().getCurrent() == State.Type.CREATED)
.findFirst();
if (lastCreated.isPresent()) {
return Optional.of(new ArrayList<>());
}
// find last termintated
Optional<TaskRun> lastTerminated = reverse
.stream()
.filter(taskRun -> taskRun.getState().isTerninated())
.findFirst();
if (lastTerminated.isPresent()) {
if (lastTerminated.get().getState().getCurrent() == State.Type.FAILED) {
log.warn("Must find errors path");
return Optional.of(new ArrayList<>());
} else {
int index = execution.getTaskRunList().indexOf(lastTerminated.get());
if (tasks.size() > index - 1) {
return Optional.of(tasks
.get(index + 1)
.toTaskRun(execution)
);
}
}
}
return Optional.of(new ArrayList<>());
}
}

View File

@@ -0,0 +1,17 @@
package org.floworc.core.executions;
import lombok.Data;
import org.slf4j.event.Level;
import java.time.Instant;
@Data
public class LogEntry {
Instant timestamp;
Level level;
String thread;
String message;
}

View File

@@ -0,0 +1,5 @@
package org.floworc.core.executions;
public class MetricEntry {
}

View File

@@ -0,0 +1,61 @@
package org.floworc.core.executions;
import com.devskiller.friendly_id.FriendlyId;
import lombok.Builder;
import lombok.Value;
import org.floworc.core.flows.State;
import org.floworc.core.tasks.Task;
import javax.validation.constraints.NotNull;
import java.util.ArrayList;
import java.util.List;
@Value
@Builder
public class TaskRun {
private String id;
@NotNull
private String executionId;
@NotNull
private String flowId;
@NotNull
private String taskId;
private Context context;
@Builder.Default
private List<LogEntry> logs = new ArrayList<>();
@Builder.Default
private List<MetricEntry> metrics = new ArrayList<>();
@NotNull
private State state;
public TaskRun withState(State.Type state) {
return new TaskRun(
this.id,
this.executionId,
this.flowId,
this.taskId,
this.context,
this.logs,
this.metrics,
this.state.withState(state)
);
}
public static TaskRun of(Execution execution, Task task) {
return TaskRun.builder()
.id(FriendlyId.createFriendlyId())
.executionId(execution.getId())
.flowId(execution.getFlowId())
.taskId(task.getId())
.state(new State())
.build();
}
}

View File

@@ -0,0 +1,28 @@
package org.floworc.core.executions;
import lombok.Builder;
import lombok.Value;
import lombok.experimental.Wither;
import org.floworc.core.tasks.Task;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.validation.constraints.NotNull;
@Value
@Builder
public class WorkerTask {
@NotNull
@Wither
private TaskRun taskRun;
@NotNull
private Task task;
public Logger logger() {
return LoggerFactory.getLogger(
"flow." + this.getTaskRun().getFlowId() + "." +
this.getTask().getId()
);
}
}

View File

@@ -0,0 +1,51 @@
package org.floworc.core.flows;
import lombok.*;
import org.floworc.core.tasks.Task;
import org.floworc.core.triggers.Trigger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import java.util.List;
import java.util.Optional;
@Value
@Builder
public class Flow {
@NotNull
private String id;
@NotNull
private String namespace;
@Valid
private List<Inputs> inputs;
@Valid
private List<Task> tasks;
@Valid
private List<Task> errors;
@Valid
private List<Trigger> triggers;
public Logger logger() {
return LoggerFactory.getLogger("flow." + this.id);
}
public Task findTaskById(String id) {
Optional<Task> find = this.tasks
.stream()
.filter(task -> task.getId().equals(id))
.findFirst();
if (!find.isPresent()) {
throw new IllegalArgumentException("Can't find task with id '" + id + "' on flow '" + this.id + "'");
}
return find.get();
}
}

View File

@@ -0,0 +1,14 @@
package org.floworc.core.flows;
import lombok.Data;
import javax.validation.constraints.NotNull;
@Data
public class Inputs {
@NotNull
private String name;
private Class type;
}

View File

@@ -0,0 +1,72 @@
package org.floworc.core.flows;
import lombok.Value;
import org.floworc.core.executions.TaskRun;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
@Value
public class State {
private Type current;
private List<History> histories;
public State() {
this.current = Type.CREATED;
this.histories = new ArrayList<>();
this.histories.add(new History(this.current, Instant.now()));
}
public State(Type state, State actual) {
this.current = state;
this.histories = actual.histories;
this.histories.add(new History(this.current, Instant.now()));
}
public State withState(Type state) {
return new State(state, this);
}
public Duration duration() {
return Duration.between(
this.histories.get(0).getDate(),
this.histories.size() > 1 ? this.histories.get(this.histories.size() - 1).getDate() : Instant.now()
);
}
public String humanDuration() {
String duration = duration()
.toString()
.substring(2)
.replaceAll("(\\d[HMS])(?!$)", " $1 ")
.toLowerCase();
return duration.substring(0, duration.length() - 4) + "s";
}
public boolean isTerninated() {
return this.current == Type.SKIPPED || this.current == Type.FAILED || this.current == Type.SUCCESS;
}
public boolean isRunning() {
return this.current == Type.RUNNING;
}
public enum Type {
CREATED,
RUNNING,
SUCCESS,
FAILED,
SKIPPED,
PAUSED;
}
@Value
public static class History {
private Type state;
private Instant date;
}
}

View File

@@ -0,0 +1,11 @@
package org.floworc.core.queues;
import java.util.function.Consumer;
public interface QueueInterface <T> {
boolean emit(QueueMessage<T> message);
void receive(Consumer<QueueMessage<T>> consumer);
void ack(QueueMessage<T> message);
}

View File

@@ -0,0 +1,14 @@
package org.floworc.core.queues;
import lombok.Builder;
import lombok.Data;
@Data
@Builder
public class QueueMessage <T> {
private String key;
private T body;
}

View File

@@ -0,0 +1,11 @@
package org.floworc.core.queues;
public enum QueueName {
WORKERS,
EXECUTIONS,
WORKERS_RESULT;
public boolean isPubSub() {
return this == EXECUTIONS;
}
}

View File

@@ -0,0 +1,61 @@
package org.floworc.core.queues.types;
import lombok.extern.slf4j.Slf4j;
import org.floworc.core.queues.QueueInterface;
import org.floworc.core.queues.QueueMessage;
import org.floworc.core.queues.QueueName;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.function.Consumer;
@Slf4j
public class LocalQueue <T> implements QueueInterface<T> {
private static ThreadPoolExecutor poolExecutor = (ThreadPoolExecutor) Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
private QueueName topic;
private List<QueueMessage<T>> messages = new ArrayList<>();
private List<Consumer<QueueMessage<T>>> consumers = new ArrayList<>();
public LocalQueue(QueueName topic) {
this.topic = topic;
}
@Override
public boolean emit(QueueMessage<T> message) {
if (log.isTraceEnabled()) {
log.trace("New message: topic '{}', key '{}', value {}", this.topic, message.getKey(), message.getBody());
}
this.messages.add(message);
if (this.consumers != null) {
if (this.topic.isPubSub()) {
this.consumers
.forEach(consumers ->
poolExecutor.execute(() ->
consumers.accept(message)
)
);
} else {
poolExecutor.execute(() -> {
this.consumers.get((new Random()).nextInt(this.consumers.size())).accept(message);
});
}
}
return true;
}
@Override
public void receive(Consumer<QueueMessage<T>> consumer) {
this.consumers.add(consumer);
}
@Override
public void ack(QueueMessage<T> message) {
this.messages.remove(message);
}
}

View File

@@ -0,0 +1,21 @@
package org.floworc.core.repositories;
import org.floworc.core.executions.Execution;
import java.util.List;
public class ExecutionRepository {
private RepositoryStorage repositoryStorage;
public ExecutionRepository(RepositoryStorage repositoryStorage) {
this.repositoryStorage = repositoryStorage;
}
public Execution getById(String id) {
return this.repositoryStorage.getByKey(Execution.class, id);
}
public List<Execution> getAll() {
return this.repositoryStorage.getAll(Execution.class);
}
}

View File

@@ -0,0 +1,12 @@
package org.floworc.core.repositories;
import org.floworc.core.flows.Flow;
import java.util.List;
import java.util.Optional;
public interface RepositoryInterface {
Optional<Flow> getFlowById(String id);
List<Flow> getFlows();
}

View File

@@ -0,0 +1,9 @@
package org.floworc.core.repositories;
import java.util.List;
public interface RepositoryStorage {
<T> T getByKey(Class<T> clz, String id);
<T> List<T> getAll(Class<T> clz);
}

View File

@@ -0,0 +1,29 @@
package org.floworc.core.repositories.storages;
import org.floworc.core.repositories.RepositoryStorage;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class MemoryStorage implements RepositoryStorage {
private Map<String, Map<String, Object>> storage = new HashMap<>();
@Override
@SuppressWarnings("unchecked")
public <T> T getByKey(Class<T> clz, String id) {
return (T) this.storage.get(clz.getName()).get(id);
}
@Override
@SuppressWarnings("unchecked")
public <T> List<T> getAll(Class<T> clz) {
return new ArrayList<>(this.storage.get(clz.getName()).values())
.stream()
.map(o -> (T) o)
.collect(Collectors.toList());
}
}

View File

@@ -0,0 +1,44 @@
package org.floworc.core.repositories.types;
import org.floworc.core.flows.Flow;
import org.floworc.core.repositories.RepositoryInterface;
import org.floworc.core.serializers.YamlFlowParser;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
public class LocalRepository implements RepositoryInterface {
private File basePath;
private static final YamlFlowParser yamlFlowParser = new YamlFlowParser();
public LocalRepository(File basePath) {
this.basePath = basePath;
}
@Override
public Optional<Flow> getFlowById(String id) {
File file = new File(this.basePath, id + ".yaml");
try {
return Optional.of(yamlFlowParser.parse(file));
} catch (IOException e) {
return Optional.empty();
}
}
@Override
public List<Flow> getFlows() {
try {
return Files.list(this.basePath.toPath())
.map(path -> this.getFlowById(path.toFile().getName()))
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toList());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}

View File

@@ -0,0 +1,57 @@
package org.floworc.core.runners;
import lombok.extern.slf4j.Slf4j;
import org.floworc.core.executions.Execution;
import org.floworc.core.executions.TaskRun;
import org.floworc.core.executions.WorkerTask;
import org.floworc.core.queues.QueueInterface;
import org.floworc.core.queues.QueueMessage;
import java.util.concurrent.ConcurrentHashMap;
@Slf4j
public class ExecutionState implements Runnable {
private QueueInterface<Execution> executionQueue;
private QueueInterface<WorkerTask> workerTaskResultQueue;
private static ConcurrentHashMap<String, Execution> executions = new ConcurrentHashMap<>();
public ExecutionState(
QueueInterface<Execution> executionQueue,
QueueInterface<WorkerTask> workerTaskResultQueue
) {
this.executionQueue = executionQueue;
this.workerTaskResultQueue = workerTaskResultQueue;
}
@Override
public void run() {
this.executionQueue.receive(message -> {
Execution execution = message.getBody();
if (execution.getState().isTerninated()) {
executions.remove(message.getKey());
} else {
executions.put(message.getKey(), execution);
}
});
this.workerTaskResultQueue.receive(message -> {
TaskRun taskRun = message.getBody().getTaskRun();
if (!executions.containsKey(taskRun.getExecutionId())) {
throw new RuntimeException("Unable to find execution '" + taskRun.getExecutionId() + "' on ExecutionState");
}
Execution execution = executions.get(taskRun.getExecutionId());
Execution newExecution = execution.withTaskRun(taskRun);
this.executionQueue.emit(
QueueMessage.<Execution>builder()
.key(newExecution.getId())
.body(newExecution)
.build()
);
});
}
}

View File

@@ -0,0 +1,125 @@
package org.floworc.core.runners;
import lombok.extern.slf4j.Slf4j;
import org.floworc.core.executions.Execution;
import org.floworc.core.executions.ExecutionService;
import org.floworc.core.executions.TaskRun;
import org.floworc.core.executions.WorkerTask;
import org.floworc.core.flows.Flow;
import org.floworc.core.flows.State;
import org.floworc.core.queues.QueueInterface;
import org.floworc.core.queues.QueueMessage;
import org.floworc.core.repositories.RepositoryInterface;
import java.util.ArrayList;
import java.util.List;
@Slf4j
public class Executor implements Runnable {
private QueueInterface<Execution> executionQueue;
private QueueInterface<WorkerTask> workerTaskQueue;
private RepositoryInterface repository;
public Executor(
QueueInterface<Execution> executionQueue,
QueueInterface<WorkerTask> workerTaskQueue,
RepositoryInterface repositoryInterface
) {
this.executionQueue = executionQueue;
this.workerTaskQueue = workerTaskQueue;
this.repository = repositoryInterface;
}
@Override
public void run() {
this.executionQueue.receive(message -> {
Execution execution = message.getBody();
if (!execution.getState().isTerninated()) {
Flow flow = this.repository
.getFlowById(execution.getFlowId())
.orElseThrow(() -> new IllegalArgumentException("Invalid flow id '" + execution.getFlowId() + "'"));
ExecutionService.getNexts(execution, flow.getTasks())
.ifPresentOrElse(
nexts -> this.onNexts(flow, execution, nexts),
() -> this.onEnd(flow, execution)
);
}
});
}
private void onNexts(Flow flow, Execution execution, List<TaskRun> nexts) {
if (nexts.size() == 0) {
flow.logger().trace(
"[execution: {}] Call getNexts but no next found: {}",
execution.getId(),
execution
);
return;
} else {
flow.logger().trace(
"[execution: {}] Found nexts {}",
execution.getId(),
nexts
);
}
List<TaskRun> executionTasksRun;
Execution newExecution;
if (execution.getTaskRunList() == null) {
executionTasksRun = nexts;
} else {
executionTasksRun = new ArrayList<>(execution.getTaskRunList());
executionTasksRun.addAll(nexts);
}
newExecution = execution.withTaskRunList(executionTasksRun);
if (execution.getState().getCurrent() == State.Type.CREATED) {
flow.logger().info(
"[execution: {}] Flow started",
execution.getId()
);
newExecution = newExecution.withState(State.Type.RUNNING);
}
this.executionQueue.emit(
QueueMessage.<Execution>builder()
.key(newExecution.getId())
.body(newExecution)
.build()
);
final Execution finalNewExecution = newExecution;
nexts.forEach(taskRun -> this.workerTaskQueue.emit(QueueMessage.<WorkerTask>builder()
.key(finalNewExecution.getId())
.body(WorkerTask.builder()
.taskRun(taskRun)
.task(flow.findTaskById(taskRun.getTaskId()))
.build()
)
.build()
));
}
private void onEnd(Flow flow, Execution execution) {
Execution newExecution = execution.withState(State.Type.SUCCESS);
flow.logger().info(
"[execution: {}] Flow completed with state {} in {}",
newExecution.getId(),
newExecution.getState().getCurrent(),
newExecution.getState().humanDuration()
);
this.executionQueue.emit(
QueueMessage.<Execution>builder()
.key(newExecution.getId())
.body(newExecution)
.build()
);
}
}

View File

@@ -0,0 +1,8 @@
package org.floworc.core.runners;
import org.floworc.core.executions.Execution;
import org.floworc.core.flows.Flow;
public interface RunnerInterface {
void run();
}

View File

@@ -0,0 +1,62 @@
package org.floworc.core.runners;
import org.floworc.core.executions.WorkerTask;
import org.floworc.core.flows.State;
import org.floworc.core.queues.QueueInterface;
import org.floworc.core.queues.QueueMessage;
public class Worker implements Runnable {
private QueueInterface<WorkerTask> workerTaskQueue;
private QueueInterface<WorkerTask> workerTaskResultQueue;
public Worker(QueueInterface<WorkerTask> workerTaskQueue, QueueInterface<WorkerTask> workerTaskResultQueue) {
this.workerTaskQueue = workerTaskQueue;
this.workerTaskResultQueue = workerTaskResultQueue;
}
@Override
public void run() {
this.workerTaskQueue.receive(message -> {
this.run(message.getBody());
});
}
public void run(WorkerTask workerTask) {
workerTask.logger().info(
"[execution: {}] [taskrun: {}] Task started",
workerTask.getTaskRun().getExecutionId(),
workerTask.getTaskRun().getId()
);
this.workerTaskResultQueue.emit(QueueMessage.<WorkerTask>builder()
.key(workerTask.getTaskRun().getExecutionId())
.body(workerTask.withTaskRun(workerTask.getTaskRun().withState(State.Type.RUNNING)))
.build()
);
try {
workerTask.getTask().run();
this.workerTaskResultQueue.emit(QueueMessage.<WorkerTask>builder()
.key(workerTask.getTaskRun().getExecutionId())
.body(workerTask.withTaskRun(workerTask.getTaskRun().withState(State.Type.SUCCESS)))
.build()
);
} catch (Exception e) {
workerTask.logger().error("Failed task", e);
this.workerTaskResultQueue.emit(QueueMessage.<WorkerTask>builder()
.key(workerTask.getTaskRun().getExecutionId())
.body(workerTask.withTaskRun(workerTask.getTaskRun().withState(State.Type.FAILED)))
.build()
);
}
workerTask.logger().info(
"[execution: {}] [taskrun: {}] Task completed in {}",
workerTask.getTaskRun().getExecutionId(),
workerTask.getTaskRun().getId(),
workerTask.getTaskRun().getState().humanDuration()
);
}
}

View File

@@ -0,0 +1,94 @@
package org.floworc.core.runners.types;
import com.devskiller.friendly_id.FriendlyId;
import lombok.extern.slf4j.Slf4j;
import org.floworc.core.executions.Execution;
import org.floworc.core.executions.WorkerTask;
import org.floworc.core.flows.Flow;
import org.floworc.core.flows.State;
import org.floworc.core.queues.QueueMessage;
import org.floworc.core.queues.QueueName;
import org.floworc.core.queues.types.LocalQueue;
import org.floworc.core.repositories.types.LocalRepository;
import org.floworc.core.runners.ExecutionState;
import org.floworc.core.runners.Executor;
import org.floworc.core.runners.RunnerInterface;
import org.floworc.core.runners.Worker;
import java.io.File;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
@Slf4j
public class StandAloneRunner implements RunnerInterface {
private LocalQueue<Execution> executionQueue;
private LocalQueue<WorkerTask> workerTaskQueue;
private LocalQueue<WorkerTask> workerTaskResultQueue;
private LocalRepository localRepository;
private ThreadPoolExecutor poolExecutor;
public StandAloneRunner(File basePath) {
this.executionQueue = new LocalQueue<>(QueueName.EXECUTIONS);
this.workerTaskQueue = new LocalQueue<>(QueueName.WORKERS);
this.workerTaskResultQueue = new LocalQueue<>(QueueName.WORKERS_RESULT);
this.localRepository = new LocalRepository(basePath);
}
@Override
public void run() {
poolExecutor = (ThreadPoolExecutor) Executors.newFixedThreadPool(5);
poolExecutor.execute(new Executor(
this.executionQueue,
this.workerTaskQueue,
this.localRepository
));
poolExecutor.execute(new ExecutionState(
this.executionQueue,
this.workerTaskResultQueue
));
while(poolExecutor.getActiveCount() != poolExecutor.getCorePoolSize()) {
poolExecutor.execute(new Worker(
this.workerTaskQueue,
this.workerTaskResultQueue
));
}
}
public Execution run(Flow flow) throws InterruptedException {
this.run();
Execution execution = Execution.builder()
.id(FriendlyId.createFriendlyId())
.flowId(flow.getId())
.state(new State())
.build();
AtomicReference<Execution> receive = new AtomicReference<>();
this.executionQueue.receive(message -> {
if (message.getBody().getState().isTerninated()) {
receive.set(message.getBody());
this.poolExecutor.shutdownNow();
}
});
this.executionQueue.emit(
QueueMessage.<Execution>builder()
.key(execution.getId())
.body(execution)
.build()
);
this.poolExecutor.awaitTermination(5, TimeUnit.MINUTES);
return receive.get();
}
}

View File

@@ -0,0 +1,28 @@
package org.floworc.core.serializers;
import lombok.Getter;
import org.floworc.core.flows.Flow;
import javax.validation.ConstraintViolation;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
@Getter
public class InvalidDefinitionException extends IllegalArgumentException {
private Set<ConstraintViolation<Flow>> violations;
private InvalidDefinitionException(String message, Set<ConstraintViolation<Flow>> violations) {
super(message);
this.violations = violations;
}
public static InvalidDefinitionException of(Set<ConstraintViolation<Flow>> violations) {
List<String> errors = violations.stream().map(Object::toString).collect(Collectors.toList());
return new InvalidDefinitionException(
"Invalid Flow definitions with errors: \n- " + String.join("\n- ", errors),
violations
);
}
}

View File

@@ -0,0 +1,40 @@
package org.floworc.core.serializers;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.hibernate.validator.messageinterpolation.ParameterMessageInterpolator;
import org.floworc.core.flows.Flow;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import java.io.File;
import java.io.IOException;
import java.util.Set;
public class YamlFlowParser {
private static final ObjectMapper mapper = new ObjectMapper(new YAMLFactory())
.registerModule(new Jdk8Module())
.registerModule(new JavaTimeModule());
private static final Validator validator = Validation.byDefaultProvider()
.configure()
.messageInterpolator(new ParameterMessageInterpolator())
.buildValidatorFactory()
.getValidator();
public Flow parse(File file) throws IOException {
Flow flow = mapper.readValue(file, Flow.class);
Set<ConstraintViolation<Flow>> violations = validator.validate(flow);
if (violations.size() > 0) {
throw InvalidDefinitionException.of(violations);
}
return flow;
}
}

View File

@@ -0,0 +1,14 @@
package org.floworc.core.tasks;
import lombok.*;
import java.time.Duration;
@Data
public class Retry {
private int limit;
private RetryIntervalType type;
private Duration interval;
}

View File

@@ -0,0 +1,6 @@
package org.floworc.core.tasks;
public enum RetryIntervalType {
CONSTANT,
EXPONENTIAL
}

View File

@@ -0,0 +1,47 @@
package org.floworc.core.tasks;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import lombok.Data;
import org.apache.avro.reflect.Nullable;
import org.floworc.core.executions.Execution;
import org.floworc.core.executions.TaskRun;
import javax.validation.constraints.NotNull;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, property = "type", visible = true)
@Data
abstract public class Task {
@NotNull
private String id;
private String type;
@Nullable
private Retry retry;
private int timeout;
private List<Task> errors;
abstract public Void run() throws Exception;
public List<TaskRun> toTaskRun(Execution execution) {
return Collections.singletonList(TaskRun.of(execution, this));
}
public Optional<List<TaskRun>> getChildTaskRun(Execution execution) {
return Optional.of(new ArrayList<>());
}
public Optional<Task> findById(String id) {
if (this.getId().equals(id)) {
return Optional.of(this);
}
return Optional.empty();
}
}

View File

@@ -0,0 +1,16 @@
package org.floworc.core.tasks;
import lombok.Data;
import org.floworc.core.flows.State;
import java.time.Duration;
import java.time.Instant;
@Data
public class TaskResult {
private State state;
private Instant start;
private Duration duration;
}

View File

@@ -0,0 +1,21 @@
package org.floworc.core.tasks.debugs;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
import org.floworc.core.tasks.Task;
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
@Data
@Slf4j
public class Echo extends Task {
private String format;
@Override
public Void run() throws Exception {
log.info(this.format);
return null;
}
}

View File

@@ -0,0 +1,16 @@
package org.floworc.core.tasks.flows;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
import java.util.List;
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
@Data
@Slf4j
public class Each extends Parallel {
private List<String> values;
}

View File

@@ -0,0 +1,50 @@
package org.floworc.core.tasks.flows;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
import org.floworc.core.executions.Execution;
import org.floworc.core.executions.ExecutionService;
import org.floworc.core.executions.TaskRun;
import org.floworc.core.tasks.Task;
import java.util.List;
import java.util.Optional;
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
@Data
@Slf4j
public class Parallel extends Task {
private Integer concurrent;
private List<Task> tasks;
@Override
public Optional<Task> findById(String id) {
Optional<Task> superFind = super.findById(id);
if (superFind.isPresent()) {
return superFind;
}
return this.tasks
.stream()
.map(task -> task.findById(id))
.filter(Optional::isPresent)
.map(Optional::get)
.findFirst();
}
@Override
public Optional<List<TaskRun>> getChildTaskRun(Execution execution) {
return ExecutionService.getNexts(execution, this.tasks);
}
@Override
public Void run() {
log.info("Starting '{}'", this.tasks);
return null;
}
}

View File

@@ -0,0 +1,27 @@
package org.floworc.core.tasks.flows;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
import org.floworc.core.tasks.Task;
import java.util.List;
import java.util.Map;
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
@Data
@Slf4j
public class Switch extends Task {
private Map<String, List<Task>> cases;
private List<Task> defaults;
@Override
public Void run() {
log.info("Starting '{}'", this);
return null;
}
}

View File

@@ -0,0 +1,72 @@
package org.floworc.core.tasks.scripts;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import lombok.Value;
import lombok.extern.slf4j.Slf4j;
import org.floworc.core.tasks.Task;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.List;
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
@Value
@Slf4j
public class Bash extends Task {
private String[] commands;
@Override
public Void run() throws Exception {
List<String> commands = Arrays.asList("/bin/sh", "-c", String.join("\n", this.commands));
log.debug("Starting command [{}]", String.join("; ", this.commands));
ProcessBuilder processBuilder = new ProcessBuilder();
processBuilder.command(commands);
Process process = processBuilder.start();
readInput(process.getInputStream(), false);
readInput(process.getErrorStream(), true);
// process.pid();
int exitCode = process.waitFor();
if (exitCode != 0) {
throw new RuntimeException("Command failed with code " + exitCode);
} else {
log.debug("Command succeed with code " + exitCode);
}
return null;
}
private void readInput(InputStream inputStream, boolean isStdErr) {
Thread thread = new Thread(() -> {
try {
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String line;
while ((line = bufferedReader.readLine()) != null) {
if (isStdErr) {
log.warn(line);
} else {
log.info(line);
}
}
} catch (IOException e) {
throw new RuntimeException(e);
}
});
thread.setName("bash-log");
thread.start();
}
}

View File

@@ -0,0 +1,8 @@
package org.floworc.core.triggers;
import lombok.Data;
@Data
abstract public class Trigger {
private String type;
}

View File

@@ -0,0 +1,13 @@
package org.floworc.core.triggers.types;
import lombok.*;
import org.floworc.core.triggers.Trigger;
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
@Data
public class Schedule extends Trigger {
private String expression;
private ScheduleBackfill backfill;
}

View File

@@ -0,0 +1,10 @@
package org.floworc.core.triggers.types;
import lombok.*;
import java.time.Instant;
@Data
public class ScheduleBackfill {
private Instant start;
}

View File

@@ -0,0 +1,23 @@
package org.floworc.core;
import org.floworc.core.flows.Flow;
import org.floworc.core.serializers.YamlFlowParser;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import static org.junit.jupiter.api.Assertions.assertNotNull;
abstract public class Utils {
private static final YamlFlowParser yamlFlowParser = new YamlFlowParser();
public static Flow parse(String path) throws IOException {
URL resource = Utils.class.getClassLoader().getResource(path);
assertNotNull(resource);
File file = new File(resource.getFile());
return yamlFlowParser.parse(file);
}
}

View File

@@ -0,0 +1,34 @@
package org.floworc.core.runners;
import org.junit.jupiter.api.Test;
import org.floworc.core.Utils;
import org.floworc.core.executions.Execution;
import org.floworc.core.flows.Flow;
import org.floworc.core.runners.types.StandAloneRunner;
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.Objects;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.hasSize;
class StandAloneRunnerTest {
private final StandAloneRunner runner = new StandAloneRunner(
new File(Objects.requireNonNull(Utils.class.getClassLoader().getResource("flows")).toURI())
);
StandAloneRunnerTest() throws URISyntaxException {
}
@Test
void run() throws IOException, InterruptedException {
Flow flow = Utils.parse("flows/full.yaml");
Execution execution = runner.run(flow);
assertThat(execution.getTaskRunList(), hasSize(4));
}
}

View File

@@ -0,0 +1,43 @@
package org.floworc.core.serializers;
import org.junit.jupiter.api.Test;
import org.floworc.core.Utils;
import org.floworc.core.flows.Flow;
import org.floworc.core.tasks.RetryIntervalType;
import org.floworc.core.tasks.Task;
import java.io.IOException;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.junit.jupiter.api.Assertions.assertThrows;
class YamlFlowParserTest {
@Test
void parse() throws IOException {
Flow flow = Utils.parse("flows/full.yaml");
assertThat(flow.getId(), is("full"));
assertThat(flow.getTasks().size(), is(4));
// third with all optionnals
Task optionnals = flow.getTasks().get(2);
assertThat(optionnals.getTimeout(), is(1000));
assertThat(optionnals.getRetry().getInterval().getSeconds(), is(900L));
assertThat(optionnals.getRetry().getType(), is(RetryIntervalType.CONSTANT));
assertThat(optionnals.getRetry().getLimit(), is(5));
}
@Test
void validation() throws IOException {
assertThrows(InvalidDefinitionException.class, () -> {
Utils.parse("flows/invalid.yaml");
});
try {
Utils.parse("flows/invalid.yaml");
} catch (InvalidDefinitionException e) {
assertThat(e.getViolations().size(), is(3));
}
}
}

View File

@@ -0,0 +1,16 @@
package org.floworc.core.tasks;
import org.junit.jupiter.api.Test;
import org.floworc.core.runners.types.StandAloneRunner;
import org.floworc.core.tasks.scripts.Bash;
class BashTest {
@Test
void run() throws Exception {
Bash bash = new Bash(
new String[]{"sleep 1", "curl www.google.fr > /dev/null", "echo 0", "sleep 1", "echo 1"}
);
bash.run();
}
}

View File

@@ -0,0 +1,107 @@
id: full
namespace: com.leroymerlin.data.supply
#triggers:
#- type: schedule
# expression: 42 4 1 * *
# backfill:
# start: 2018-01-01
#
tasks:
- id: 1st
type: org.floworc.core.tasks.scripts.Bash
commands:
- 'echo "first"'
- id: 2nd
type: org.floworc.core.tasks.debugs.Echo
format: second {{todo}}
- id: 3rd
type: org.floworc.core.tasks.debugs.Echo
format: third all optionnal args {{todo}}
timeout: 1000
retry:
limit: 5
type: CONSTANT
interval: PT15M
- id: 4th
type: org.floworc.core.tasks.flows.Parallel
concurrent: 3
tasks:
- id: 4th - 1st
type: org.floworc.core.tasks.scripts.Bash
commands:
- echo "{{id}}"
- id: 4th - 2nd
type: org.floworc.core.tasks.scripts.Bash
commands:
- echo "{{id}}"
- id: 4th - 3rd
type: org.floworc.core.tasks.scripts.Bash
commands:
- echo "{{id}}"
- id: 4th - 4th
type: org.floworc.core.tasks.scripts.Bash
commands:
- echo "{{id}}"
- id: 4th - 5th
type: org.floworc.core.tasks.scripts.Bash
commands:
- echo "{{id}}"
#- type: python
# commands:
# - print(bla)
#- type: each
# values: {{selector}}
# tasks:
# - type: org.floworc.core.tasks.scripts.Bash
# commands:
# - echo {{item}}
#
#- type: switch
# expression: {{selector}}
# cases:
# - value: hello
# tasks:
# - type: org.floworc.core.tasks.debugs.Echo
# text: hello world
# - value: bye
# tasks:
# - type: org.floworc.core.tasks.debugs.Echo
# text: goodbye world
# default:
# - tasks:
# -type: org.floworc.core.tasks.debugs.Echo
# text: something else
#
#- type: branch
# branches:
# first:
# - type: org.floworc.core.tasks.scripts.Bash
# commands:
# - echo "first branch - step 1"
# - type: org.floworc.core.tasks.scripts.Bash
# commands:
# - echo "first branch - step 2"
# second:
# - type: org.floworc.core.tasks.scripts.Bash
# commands:
# - echo "second branch - step 1"
# - type: org.floworc.core.tasks.scripts.Bash
# commands:
# - echo "second branch - step 2"
#
#- type: org.floworc.core.tasks.scripts.Bash
# commands: sleep 5
# timeout: 1
# errors:
# - type: org.floworc.core.tasks.scripts.Bash
# commands:
# - echo "first branch - step 1"
# - type: org.floworc.core.tasks.scripts.Bash
# commands:
# - echo "first branch - step 2"

View File

@@ -0,0 +1,4 @@
tasks:
- type: org.floworc.core.tasks.scripts.Bash
commands:
- echo "bla"

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">
<!-- Remove logback startup log -->
<statusListener class="ch.qos.logback.core.status.NopStatusListener" />
<property name="pattern" value="%d{ISO8601} %highlight(%-5.5level) %magenta(%-12.12thread) %cyan(%-12.12logger{12}) %msg%n" />
<withJansi>true</withJansi>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<target>System.out</target>
<immediateFlush>true</immediateFlush>
<encoder>
<pattern>${pattern}</pattern>
</encoder>
</appender>
<root level="WARN">
<appender-ref ref="STDOUT" />
</root>
<logger name="org.floworc" level="DEBUG" />
<logger name="flow" level="DEBUG" />
</configuration>

BIN
gradle/wrapper/gradle-wrapper.jar vendored Normal file

Binary file not shown.

View File

@@ -0,0 +1,6 @@
#Thu Apr 25 10:26:30 CEST 2019
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip

172
gradlew vendored Executable file
View File

@@ -0,0 +1,172 @@
#!/usr/bin/env sh
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"

84
gradlew.bat vendored Executable file
View File

@@ -0,0 +1,84 @@
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

2
lombok.config Normal file
View File

@@ -0,0 +1,2 @@
lombok.addLombokGeneratedAnnotation = true
lombok.anyConstructor.addConstructorProperties = true

4
settings.gradle Normal file
View File

@@ -0,0 +1,4 @@
rootProject.name="floworc"
include 'cli'
include 'core'