// Path: bootique-job/src/main/java/io/bootique/job/Job.java
// public interface Job {
// 
// 	JobMetadata getMetadata();
// 
// 	JobResult run(Map<String, Object> params);
// }
// 
// Path: bootique-job/src/main/java/io/bootique/job/JobRegistry.java
// public interface JobRegistry {
// 
//     /**
//      * @deprecated since 3.0 in favor of {@link #getJobNames()}
//      */
//     default Set<String> getAvailableJobs() {
//         return getJobNames();
//     }
// 
//     /**
//      * Returns all known job names, including standalone jobs and job groups.
//      *
//      * @since 3.0.M1
//      */
//     Set<String> getJobNames();
// 
//     Job getJob(String jobName);
// 
//     boolean allowsSimultaneousExecutions(String jobName);
// }
// 
// Path: bootique-job/src/main/java/io/bootique/job/runnable/JobFuture.java
// public interface JobFuture extends ScheduledFuture<JobResult> {
// 
//     static JobFutureBuilder forJob(String job) {
//         return new JobFutureBuilder(job);
//     }
// 
//     /**
//      * @return Job name
//      */
//     String getJobName();
// 
//     /**
//      * Waits till the job is done and then returns the result.
//      */
//     @Override
//     JobResult get();
// 
//     /**
//      * Waits till the job is done and then returns the result.
//      * Throws an exception, if time elapses before the job has finished.
//      */
//     @Override
//     JobResult get(long timeout, TimeUnit unit);
// 
//     /**
//      * Convenient shortcut for {@link java.util.concurrent.Future#cancel(boolean)}.
//      *
//      * @return {@code false} if the task could not be cancelled,
//      * typically because it has already completed normally;
//      * {@code true} otherwise
//      */
//     default boolean cancelInterruptibly() {
//         return cancel(true);
//     }
// }
// 
// Path: bootique-job/src/main/java/io/bootique/job/runnable/RunnableJob.java
// public interface RunnableJob {
// 
// 	JobResult run();
// }
// 
// Path: bootique-job/src/main/java/io/bootique/job/runnable/RunnableJobFactory.java
// public interface RunnableJobFactory {
// 
// 	/**
// 	 * Creates a {@link RunnableJob} object that combines job instance with a
// 	 * set of parameters.
// 	 * 
// 	 * @param job
// 	 *            A job instance to run when the returned {@link RunnableJob} is
// 	 *            executed.
// 	 * @param parameters
// 	 *            A set of parameters to apply to the job when the returned
// 	 *            {@link RunnableJob} is executed.
// 	 * @return a wrapper around a job and a set of parameters.
// 	 */
// 	RunnableJob runnable(Job job, Map<String, Object> parameters);
// }
// 
// Path: bootique-job/src/main/java/io/bootique/job/value/Cron.java
// public class Cron {
// 
// 	private String expression;
// 
// 	public Cron(String expression) {
// 		this.expression = expression;
// 	}
// 
// 	@JsonValue
// 	public String getExpression() {
// 		return expression;
// 	}
// 
// 	@Override
// 	public boolean equals(Object o) {
// 		if (this == o) return true;
// 		if (o == null || getClass() != o.getClass()) return false;
// 		Cron cron = (Cron) o;
// 		return Objects.equals(expression, cron.expression);
// 	}
// 
// 	@Override
// 	public int hashCode() {
// 		if (expression == null) {
// 			return 0;
// 		}
// 
// 		return Objects.hash(expression);
// 	}
// 
// 	@Override
// 	public String toString() {
// 		return "Cron {" +
// 				"expression='" + expression + '\'' +
// 				'}';
// 	}
// }


import io.bootique.BootiqueException;
import io.bootique.job.Job;
import io.bootique.job.JobRegistry;
import io.bootique.job.runnable.JobFuture;
import io.bootique.job.runnable.JobResult;
import io.bootique.job.runnable.RunnableJob;
import io.bootique.job.runnable.RunnableJobFactory;
import io.bootique.job.value.Cron;
import io.bootique.value.Duration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.Trigger;
import java.util.*;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.stream.Collectors;
                "Trigger is misconfigured. Either of 'cron', 'fixedDelayMs', 'fixedRateMs' must be set.");
    }

    @Override
    public boolean isStarted() {
        return started.get();
    }

    @Override
    public Collection<ScheduledJobFuture> getScheduledJobs() {
        return scheduledJobsByName.values().stream().flatMap(Collection::stream).collect(Collectors.toList());
    }

    @Override
    public Collection<ScheduledJobFuture> getScheduledJobs(String jobName) {
        return scheduledJobsByName.getOrDefault(jobName, Collections.emptyList());
    }

    @Override
    public JobFuture runOnce(String jobName) {
        return runOnce(jobRegistry.getJob(jobName));
    }

    @Override
    public JobFuture runOnce(String jobName, Map<String, Object> parameters) {
        Job job = jobRegistry.getJob(jobName);
        return runOnce(job, parameters);
    }

    @Override
// Path: bootique-job/src/main/java/io/bootique/job/Job.java
// public interface Job {
// 
// 	JobMetadata getMetadata();
// 
// 	JobResult run(Map<String, Object> params);
// }
// 
// Path: bootique-job/src/main/java/io/bootique/job/JobRegistry.java
// public interface JobRegistry {
// 
//     /**
//      * @deprecated since 3.0 in favor of {@link #getJobNames()}
//      */
//     default Set<String> getAvailableJobs() {
//         return getJobNames();
//     }
// 
//     /**
//      * Returns all known job names, including standalone jobs and job groups.
//      *
//      * @since 3.0.M1
//      */
//     Set<String> getJobNames();
// 
//     Job getJob(String jobName);
// 
//     boolean allowsSimultaneousExecutions(String jobName);
// }
// 
// Path: bootique-job/src/main/java/io/bootique/job/runnable/JobFuture.java
// public interface JobFuture extends ScheduledFuture<JobResult> {
// 
//     static JobFutureBuilder forJob(String job) {
//         return new JobFutureBuilder(job);
//     }
// 
//     /**
//      * @return Job name
//      */
//     String getJobName();
// 
//     /**
//      * Waits till the job is done and then returns the result.
//      */
//     @Override
//     JobResult get();
// 
//     /**
//      * Waits till the job is done and then returns the result.
//      * Throws an exception, if time elapses before the job has finished.
//      */
//     @Override
//     JobResult get(long timeout, TimeUnit unit);
// 
//     /**
//      * Convenient shortcut for {@link java.util.concurrent.Future#cancel(boolean)}.
//      *
//      * @return {@code false} if the task could not be cancelled,
//      * typically because it has already completed normally;
//      * {@code true} otherwise
//      */
//     default boolean cancelInterruptibly() {
//         return cancel(true);
//     }
// }
// 
// Path: bootique-job/src/main/java/io/bootique/job/runnable/RunnableJob.java
// public interface RunnableJob {
// 
// 	JobResult run();
// }
// 
// Path: bootique-job/src/main/java/io/bootique/job/runnable/RunnableJobFactory.java
// public interface RunnableJobFactory {
// 
// 	/**
// 	 * Creates a {@link RunnableJob} object that combines job instance with a
// 	 * set of parameters.
// 	 * 
// 	 * @param job
// 	 *            A job instance to run when the returned {@link RunnableJob} is
// 	 *            executed.
// 	 * @param parameters
// 	 *            A set of parameters to apply to the job when the returned
// 	 *            {@link RunnableJob} is executed.
// 	 * @return a wrapper around a job and a set of parameters.
// 	 */
// 	RunnableJob runnable(Job job, Map<String, Object> parameters);
// }
// 
// Path: bootique-job/src/main/java/io/bootique/job/value/Cron.java
// public class Cron {
// 
// 	private String expression;
// 
// 	public Cron(String expression) {
// 		this.expression = expression;
// 	}
// 
// 	@JsonValue
// 	public String getExpression() {
// 		return expression;
// 	}
// 
// 	@Override
// 	public boolean equals(Object o) {
// 		if (this == o) return true;
// 		if (o == null || getClass() != o.getClass()) return false;
// 		Cron cron = (Cron) o;
// 		return Objects.equals(expression, cron.expression);
// 	}
// 
// 	@Override
// 	public int hashCode() {
// 		if (expression == null) {
// 			return 0;
// 		}
// 
// 		return Objects.hash(expression);
// 	}
// 
// 	@Override
// 	public String toString() {
// 		return "Cron {" +
// 				"expression='" + expression + '\'' +
// 				'}';
// 	}
// }


// Path: bootique-job/src/main/java/io/bootique/job/scheduler/DefaultScheduler.java
import io.bootique.BootiqueException;
import io.bootique.job.Job;
import io.bootique.job.JobRegistry;
import io.bootique.job.runnable.JobFuture;
import io.bootique.job.runnable.JobResult;
import io.bootique.job.runnable.RunnableJob;
import io.bootique.job.runnable.RunnableJobFactory;
import io.bootique.job.value.Cron;
import io.bootique.value.Duration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.Trigger;
import java.util.*;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.stream.Collectors;

                "Trigger is misconfigured. Either of 'cron', 'fixedDelayMs', 'fixedRateMs' must be set.");
    }

    @Override
    public boolean isStarted() {
        return started.get();
    }

    @Override
    public Collection<ScheduledJobFuture> getScheduledJobs() {
        return scheduledJobsByName.values().stream().flatMap(Collection::stream).collect(Collectors.toList());
    }

    @Override
    public Collection<ScheduledJobFuture> getScheduledJobs(String jobName) {
        return scheduledJobsByName.getOrDefault(jobName, Collections.emptyList());
    }

    @Override
    public JobFuture runOnce(String jobName) {
        return runOnce(jobRegistry.getJob(jobName));
    }

    @Override
    public JobFuture runOnce(String jobName, Map<String, Object> parameters) {
        Job job = jobRegistry.getJob(jobName);
        return runOnce(job, parameters);
    }

    @Override
    public JobFuture runOnce(Job job) {