Introducing the `Trigger` notion

Today every `watch` is associated with a `schedule`. When the watch is added to the system, its schedule is registered with the `scheduler` that is responsible to trigger the watch based on the schedule. This is effectively time based triggering of a `watch`.

Thinking about it further, triggering a watch is a higher abstraction than the schedule. Many things can potentially trigger a watch - a schedule (or time based triggering) is just one example of such trigger.

A `Trigger` was added to provide this abstraction. A `watch` is associated with a `trigger` not with a `schedule` directly. One type of `trigger` that can be set on a watch is a `schedule`.

This abstraction will enable us much flexibility in the future as we'll be able to add other types of triggers that are not necessarily based on time. 3 examples:

- we're planning to have a API that executes triggers on demand (rather than waiting for them to be triggered "naturally"). We could have a `"passive"` trigger with the intention to have a watch that can only be executed on demand. Today (with schedule only) you can achieve this by setting a `cron` schedule that is set to trigger very far in the future - but it's a hack.

- In the future we plan to have changes API in elasticsearch. An interesting trigger that we might want to add is `"changes"` - an ESP (event-stream processing) trigger that listens to all (data) events in the changes API, processes them and using some sort of state machine decides to trigger a watch based on some condition.

- With Shield we have audit trails. currently the only audit trail that is supported is log based (access logs). Another audit trail we'll add soon will be index based (indexing the audit info to elasticsearch). In the future, we might want to have `watcher` extend shield and add a `"watcher"` audit trail. this will effectively be a `"audit"` trigger that will trigger watches based on events arriving in the audit trail (think about notifying at real-time about a potential DDoS attack)

To support this change, we needed to change existing and introduce new constructs:

- A `Trigger` defines the logic of when a watch should be triggered
- A `TriggerEngine` is responsible for executing the logic defined by the `Trigger`
- A `TriggerEvent` is created whenever the engine triggers the trigger. The event holds relevant information (may be different for every trigger depending on its type and nature).
- A `TriggerService` manages trigger engines.

We currently have a single engine implementation - a `"scheduler"` trigger

- `ScheduleTrigger` defines a clock/calendar based schedule on which a watch should be triggered
- `QuartzScheduleEngine` a trigger engine that runs quartz scheduler which triggers the registered schedules.
- `ScheduleTriggerEvent` holds the `triggered_time` (the time the trigger was actually triggered) and the `scheduled_time` (the time the trigger was scheduled to trigger)

- Updated the docs

Closes elastic/elasticsearch#158

Original commit: elastic/x-pack-elasticsearch@5be20917cc
This commit is contained in:
uboness 2015-03-29 00:57:13 +01:00
parent c1fe5378aa
commit a632d57803
78 changed files with 1278 additions and 685 deletions

View File

@ -17,7 +17,6 @@ import org.elasticsearch.watcher.condition.ConditionModule;
import org.elasticsearch.watcher.history.HistoryModule;
import org.elasticsearch.watcher.input.InputModule;
import org.elasticsearch.watcher.rest.WatcherRestModule;
import org.elasticsearch.watcher.scheduler.SchedulerModule;
import org.elasticsearch.watcher.shield.WatcherShieldModule;
import org.elasticsearch.watcher.support.TemplateUtils;
import org.elasticsearch.watcher.support.clock.ClockModule;
@ -25,6 +24,7 @@ import org.elasticsearch.watcher.support.init.InitializingModule;
import org.elasticsearch.watcher.support.template.TemplateModule;
import org.elasticsearch.watcher.transform.TransformModule;
import org.elasticsearch.watcher.transport.WatcherTransportModule;
import org.elasticsearch.watcher.trigger.TriggerModule;
import org.elasticsearch.watcher.watch.WatchModule;
@ -46,7 +46,7 @@ public class WatcherModule extends AbstractModule implements SpawnModules {
new WatcherClientModule(),
new TransformModule(),
new WatcherRestModule(),
new SchedulerModule(),
new TriggerModule(),
new WatcherTransportModule(),
new ConditionModule(),
new InputModule(),

View File

@ -5,13 +5,13 @@
*/
package org.elasticsearch.watcher.client;
import org.elasticsearch.watcher.trigger.Trigger;
import org.elasticsearch.watcher.watch.Watch;
import org.elasticsearch.watcher.actions.Action;
import org.elasticsearch.watcher.condition.Condition;
import org.elasticsearch.watcher.condition.ConditionBuilders;
import org.elasticsearch.watcher.input.Input;
import org.elasticsearch.watcher.input.NoneInput;
import org.elasticsearch.watcher.scheduler.schedule.Schedule;
import org.elasticsearch.watcher.transform.Transform;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.unit.TimeValue;
@ -35,7 +35,7 @@ public class WatchSourceBuilder implements ToXContent {
return new WatchSourceBuilder();
}
private Schedule schedule;
private Trigger.SourceBuilder trigger;
private Input.SourceBuilder input = NoneInput.SourceBuilder.INSTANCE;
private Condition.SourceBuilder condition = ConditionBuilders.alwaysTrueCondition();
private Transform.SourceBuilder transform = null;
@ -43,8 +43,8 @@ public class WatchSourceBuilder implements ToXContent {
private TimeValue throttlePeriod = null;
private Map<String, Object> metadata;
public WatchSourceBuilder schedule(Schedule schedule) {
this.schedule = schedule;
public WatchSourceBuilder trigger(Trigger.SourceBuilder trigger) {
this.trigger = trigger;
return this;
}
@ -82,8 +82,8 @@ public class WatchSourceBuilder implements ToXContent {
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject();
builder.startObject(Watch.Parser.SCHEDULE_FIELD.getPreferredName())
.field(schedule.type(), schedule)
builder.startObject(Watch.Parser.TRIGGER_FIELD.getPreferredName())
.field(trigger.type(), trigger)
.endObject();
builder.startObject(Watch.Parser.INPUT_FIELD.getPreferredName())

View File

@ -6,23 +6,24 @@
package org.elasticsearch.watcher.history;
import org.elasticsearch.ElasticsearchIllegalStateException;
import org.elasticsearch.watcher.actions.Action;
import org.elasticsearch.watcher.condition.Condition;
import org.elasticsearch.watcher.input.Input;
import org.elasticsearch.watcher.scheduler.Scheduler;
import org.elasticsearch.watcher.support.Callback;
import org.elasticsearch.watcher.support.clock.Clock;
import org.elasticsearch.watcher.throttle.Throttler;
import org.elasticsearch.watcher.transform.Transform;
import org.elasticsearch.cluster.ClusterChangedEvent;
import org.elasticsearch.cluster.ClusterService;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.ClusterStateListener;
import org.elasticsearch.common.component.AbstractComponent;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.joda.time.DateTime;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.concurrent.EsRejectedExecutionException;
import org.elasticsearch.watcher.actions.Action;
import org.elasticsearch.watcher.condition.Condition;
import org.elasticsearch.watcher.input.Input;
import org.elasticsearch.watcher.support.Callback;
import org.elasticsearch.watcher.support.clock.Clock;
import org.elasticsearch.watcher.throttle.Throttler;
import org.elasticsearch.watcher.transform.Transform;
import org.elasticsearch.watcher.trigger.TriggerEngine;
import org.elasticsearch.watcher.trigger.TriggerEvent;
import org.elasticsearch.watcher.trigger.TriggerService;
import org.elasticsearch.watcher.watch.*;
import java.io.IOException;
@ -48,7 +49,7 @@ public class HistoryService extends AbstractComponent {
@Inject
public HistoryService(Settings settings, HistoryStore historyStore, WatchExecutor executor,
WatchStore watchStore, WatchLockService watchLockService, Scheduler scheduler,
WatchStore watchStore, WatchLockService watchLockService, TriggerService triggerService,
ClusterService clusterService, Clock clock) {
super(settings);
this.historyStore = historyStore;
@ -57,7 +58,7 @@ public class HistoryService extends AbstractComponent {
this.watchLockService = watchLockService;
this.clusterService = clusterService;
this.clock = clock;
scheduler.addListener(new SchedulerListener());
triggerService.register(new SchedulerListener());
}
public void start(ClusterState state, Callback<ClusterState> callback) {
@ -106,11 +107,11 @@ public class HistoryService extends AbstractComponent {
return executor.largestPoolSize();
}
void execute(Watch watch, DateTime scheduledFireTime, DateTime fireTime) throws HistoryException {
void execute(Watch watch, TriggerEvent event) throws HistoryException {
if (!started.get()) {
throw new ElasticsearchIllegalStateException("not started");
}
WatchRecord watchRecord = new WatchRecord(watch, scheduledFireTime, fireTime);
WatchRecord watchRecord = new WatchRecord(watch, event);
logger.debug("saving watch record [{}]", watch.name());
historyStore.put(watchRecord);
executeAsync(watchRecord, watch);
@ -223,7 +224,7 @@ public class HistoryService extends AbstractComponent {
try {
watchRecord.update(WatchRecord.State.CHECKING, null);
logger.debug("checking watch [{}]", watchRecord.name());
WatchExecutionContext ctx = new WatchExecutionContext(watchRecord.id(), watch, clock.now(), watchRecord.fireTime(), watchRecord.scheduledTime());
WatchExecutionContext ctx = new WatchExecutionContext(watchRecord.id(), watch, clock.now(), watchRecord.triggerEvent());
WatchExecution execution = execute(ctx);
watchRecord.seal(execution);
historyStore.update(watchRecord);
@ -247,9 +248,10 @@ public class HistoryService extends AbstractComponent {
}
private class SchedulerListener implements Scheduler.Listener {
private class SchedulerListener implements TriggerEngine.Listener {
@Override
public void fire(String name, DateTime scheduledFireTime, DateTime fireTime) {
public void triggered(String name, TriggerEvent event) {
if (!started.get()) {
throw new ElasticsearchIllegalStateException("not started");
}
@ -259,7 +261,7 @@ public class HistoryService extends AbstractComponent {
return;
}
try {
HistoryService.this.execute(watch, scheduledFireTime, fireTime);
HistoryService.this.execute(watch, event);
} catch (Exception e) {
logger.error("failed to execute watch [{}]", e, name);
}

View File

@ -58,7 +58,7 @@ public class HistoryStore extends AbstractComponent {
}
public void put(WatchRecord watchRecord) throws HistoryException {
String index = getHistoryIndexNameForTime(watchRecord.scheduledTime());
String index = getHistoryIndexNameForTime(watchRecord.triggerEvent().triggeredTime());
try {
IndexRequest request = new IndexRequest(index, DOC_TYPE, watchRecord.id())
.source(XContentFactory.jsonBuilder().value(watchRecord))
@ -74,7 +74,7 @@ public class HistoryStore extends AbstractComponent {
logger.debug("updating watch record [{}]...", watchRecord);
try {
BytesReference bytes = XContentFactory.jsonBuilder().value(watchRecord).bytes();
IndexRequest request = new IndexRequest(getHistoryIndexNameForTime(watchRecord.scheduledTime()), DOC_TYPE, watchRecord.id())
IndexRequest request = new IndexRequest(getHistoryIndexNameForTime(watchRecord.triggerEvent().triggeredTime()), DOC_TYPE, watchRecord.id())
.source(bytes, true)
.version(watchRecord.version());
IndexResponse response = client.index(request);

View File

@ -6,8 +6,16 @@
package org.elasticsearch.watcher.history;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.watcher.watch.Watch;
import org.elasticsearch.watcher.watch.WatchExecution;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.component.AbstractComponent;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentHelper;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.watcher.WatcherException;
import org.elasticsearch.watcher.WatcherSettingsException;
import org.elasticsearch.watcher.actions.ActionRegistry;
@ -16,17 +24,10 @@ import org.elasticsearch.watcher.condition.ConditionRegistry;
import org.elasticsearch.watcher.input.Input;
import org.elasticsearch.watcher.input.InputRegistry;
import org.elasticsearch.watcher.transform.TransformRegistry;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.component.AbstractComponent;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.joda.time.DateTime;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentHelper;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.watcher.trigger.TriggerEvent;
import org.elasticsearch.watcher.trigger.TriggerService;
import org.elasticsearch.watcher.watch.Watch;
import org.elasticsearch.watcher.watch.WatchExecution;
import java.io.IOException;
import java.util.Locale;
@ -37,8 +38,7 @@ public class WatchRecord implements ToXContent {
private String id;
private String name;
private DateTime fireTime;
private DateTime scheduledTime;
private TriggerEvent triggerEvent;
private Input input;
private Condition condition;
private State state;
@ -55,11 +55,10 @@ public class WatchRecord implements ToXContent {
WatchRecord() {
}
public WatchRecord(Watch watch, DateTime scheduledTime, DateTime fireTime) {
this.id = watch.name() + "#" + scheduledTime.toDateTimeISO();
public WatchRecord(Watch watch, TriggerEvent triggerEvent) {
this.id = watch.name() + "#" + triggerEvent.triggeredTime().toDateTimeISO();
this.name = watch.name();
this.fireTime = fireTime;
this.scheduledTime = scheduledTime;
this.triggerEvent = triggerEvent;
this.condition = watch.condition();
this.input = watch.input();
this.state = State.AWAITS_EXECUTION;
@ -71,18 +70,14 @@ public class WatchRecord implements ToXContent {
return id;
}
public DateTime scheduledTime() {
return scheduledTime;
public TriggerEvent triggerEvent() {
return triggerEvent;
}
public String name() {
return name;
}
public DateTime fireTime() {
return fireTime;
}
public Input input() { return input; }
public Condition condition() {
@ -129,27 +124,28 @@ public class WatchRecord implements ToXContent {
}
@Override
public XContentBuilder toXContent(XContentBuilder historyEntry, Params params) throws IOException {
historyEntry.startObject();
historyEntry.field(Parser.WATCH_NAME_FIELD.getPreferredName(), name);
historyEntry.field(Parser.FIRE_TIME_FIELD.getPreferredName(), fireTime.toDateTimeISO());
historyEntry.field(Parser.SCHEDULED_FIRE_TIME_FIELD.getPreferredName(), scheduledTime.toDateTimeISO());
historyEntry.startObject(Watch.Parser.CONDITION_FIELD.getPreferredName()).field(condition.type(), condition, params).endObject();
historyEntry.field(Parser.STATE_FIELD.getPreferredName(), state.id());
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject();
builder.field(Parser.WATCH_NAME_FIELD.getPreferredName(), name);
builder.startObject(Parser.TRIGGER_EVENT_FIELD.getPreferredName())
.field(triggerEvent.type(), triggerEvent)
.endObject();
builder.startObject(Watch.Parser.CONDITION_FIELD.getPreferredName()).field(condition.type(), condition, params).endObject();
builder.field(Parser.STATE_FIELD.getPreferredName(), state.id());
if (message != null) {
historyEntry.field(Parser.MESSAGE_FIELD.getPreferredName(), message);
builder.field(Parser.MESSAGE_FIELD.getPreferredName(), message);
}
if (metadata != null) {
historyEntry.field(Parser.METADATA_FIELD.getPreferredName(), metadata);
builder.field(Parser.METADATA_FIELD.getPreferredName(), metadata);
}
if (execution != null) {
historyEntry.field(Parser.WATCH_EXECUTION_FIELD.getPreferredName(), execution);
builder.field(Parser.WATCH_EXECUTION_FIELD.getPreferredName(), execution);
}
historyEntry.endObject();
return historyEntry;
builder.endObject();
return builder;
}
@Override
@ -203,8 +199,7 @@ public class WatchRecord implements ToXContent {
public static class Parser extends AbstractComponent {
public static final ParseField WATCH_NAME_FIELD = new ParseField("watch_name");
public static final ParseField FIRE_TIME_FIELD = new ParseField("fire_time");
public static final ParseField SCHEDULED_FIRE_TIME_FIELD = new ParseField("scheduled_fire_time");
public static final ParseField TRIGGER_EVENT_FIELD = new ParseField("trigger_event");
public static final ParseField MESSAGE_FIELD = new ParseField("message");
public static final ParseField STATE_FIELD = new ParseField("state");
public static final ParseField METADATA_FIELD = new ParseField("meta");
@ -214,15 +209,17 @@ public class WatchRecord implements ToXContent {
private final ActionRegistry actionRegistry;
private final InputRegistry inputRegistry;
private final TransformRegistry transformRegistry;
private final TriggerService triggerService;
@Inject
public Parser(Settings settings, ConditionRegistry conditionRegistry, ActionRegistry actionRegistry,
InputRegistry inputRegistry, TransformRegistry transformRegistry) {
InputRegistry inputRegistry, TransformRegistry transformRegistry, TriggerService triggerService) {
super(settings);
this.conditionRegistry = conditionRegistry;
this.actionRegistry = actionRegistry;
this.inputRegistry = inputRegistry;
this.transformRegistry = transformRegistry;
this.triggerService = triggerService;
}
public WatchRecord parse(BytesReference source, String historyId, long version) {
@ -253,16 +250,14 @@ public class WatchRecord implements ToXContent {
record.metadata = parser.map();
} else if (WATCH_EXECUTION_FIELD.match(currentFieldName)) {
record.execution = WatchExecution.Parser.parse(parser, conditionRegistry, actionRegistry, inputRegistry, transformRegistry);
} else if (TRIGGER_EVENT_FIELD.match(currentFieldName)) {
record.triggerEvent = triggerService.parseTriggerEvent(id, parser);
} else {
throw new WatcherException("unable to parse watch record. unexpected field [" + currentFieldName + "]");
}
} else if (token.isValue()) {
if (WATCH_NAME_FIELD.match(currentFieldName)) {
record.name = parser.text();
} else if (FIRE_TIME_FIELD.match(currentFieldName)) {
record.fireTime = DateTime.parse(parser.text());
} else if (SCHEDULED_FIRE_TIME_FIELD.match(currentFieldName)) {
record.scheduledTime = DateTime.parse(parser.text());
} else if (MESSAGE_FIELD.match(currentFieldName)) {
record.message = parser.textOrNull();
} else if (STATE_FIELD.match(currentFieldName)) {

View File

@ -8,22 +8,11 @@ package org.elasticsearch.watcher.input.search;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.watcher.watch.WatchExecutionContext;
import org.elasticsearch.watcher.watch.Payload;
import org.elasticsearch.watcher.input.Input;
import org.elasticsearch.watcher.input.InputException;
import org.elasticsearch.watcher.support.WatcherUtils;
import org.elasticsearch.watcher.support.SearchRequestEquivalence;
import org.elasticsearch.watcher.support.Variables;
import org.elasticsearch.watcher.support.init.proxy.ClientProxy;
import org.elasticsearch.watcher.support.init.proxy.ScriptServiceProxy;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.collect.MapBuilder;
import org.elasticsearch.common.component.AbstractComponent;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.joda.time.DateTime;
import org.elasticsearch.common.logging.ESLogger;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentBuilder;
@ -32,13 +21,19 @@ import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.script.ExecutableScript;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.watcher.input.Input;
import org.elasticsearch.watcher.input.InputException;
import org.elasticsearch.watcher.support.SearchRequestEquivalence;
import org.elasticsearch.watcher.support.Variables;
import org.elasticsearch.watcher.support.WatcherUtils;
import org.elasticsearch.watcher.support.init.proxy.ClientProxy;
import org.elasticsearch.watcher.support.init.proxy.ScriptServiceProxy;
import org.elasticsearch.watcher.watch.Payload;
import org.elasticsearch.watcher.watch.WatchExecutionContext;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import static org.elasticsearch.watcher.support.WatcherDateUtils.formatDate;
/**
* An input that executes search and returns the search response as the initial payload
*/
@ -68,7 +63,7 @@ public class SearchInput extends Input<SearchInput.Result> {
@Override
public Result execute(WatchExecutionContext ctx) throws IOException {
SearchRequest request = createSearchRequestWithTimes(this.searchRequest, ctx.scheduledTime(), ctx.fireTime(), ctx.executionTime(), scriptService);
SearchRequest request = createSearchRequestWithTimes(this.searchRequest, ctx, scriptService);
if (logger.isTraceEnabled()) {
logger.trace("[{}] running query for [{}] [{}]", ctx.id(), ctx.watch().name(), XContentHelper.convertToJson(request.source(), false, true));
}
@ -111,25 +106,20 @@ public class SearchInput extends Input<SearchInput.Result> {
/**
* Creates a new search request applying the scheduledFireTime and fireTime to the original request
*/
public static SearchRequest createSearchRequestWithTimes(SearchRequest requestPrototype, DateTime scheduledFireTime, DateTime fireTime, DateTime executionTime, ScriptServiceProxy scriptService) throws IOException {
public static SearchRequest createSearchRequestWithTimes(SearchRequest requestPrototype, WatchExecutionContext ctx, ScriptServiceProxy scriptService) throws IOException {
SearchRequest request = new SearchRequest(requestPrototype)
.indicesOptions(requestPrototype.indicesOptions())
.searchType(requestPrototype.searchType())
.indices(requestPrototype.indices());
if (Strings.hasLength(requestPrototype.source())) {
Map<String, String> templateParams = new HashMap<>();
templateParams.put(Variables.SCHEDULED_FIRE_TIME, formatDate(scheduledFireTime));
templateParams.put(Variables.FIRE_TIME, formatDate(fireTime));
templateParams.put(Variables.EXECUTION_TIME, formatDate(executionTime));
Map<String, Object> templateParams = Variables.createCtxModel(ctx, null);
String requestSource = XContentHelper.convertToJson(requestPrototype.source(), false);
ExecutableScript script = scriptService.executable("mustache", requestSource, ScriptService.ScriptType.INLINE, templateParams);
request.source((BytesReference) script.unwrap(script.run()), false);
} else if (requestPrototype.templateName() != null) {
MapBuilder<String, Object> templateParams = MapBuilder.newMapBuilder(requestPrototype.templateParams())
.put(Variables.SCHEDULED_FIRE_TIME, formatDate(scheduledFireTime))
.put(Variables.FIRE_TIME, formatDate(fireTime))
.put(Variables.EXECUTION_TIME, formatDate(executionTime));
request.templateParams(templateParams.map());
Map<String, Object> templateParams = Variables.createCtxModel(ctx, null);
templateParams.putAll(requestPrototype.templateParams());
request.templateParams(templateParams);
request.templateName(requestPrototype.templateName());
request.templateType(requestPrototype.templateType());
}

View File

@ -1,53 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.watcher.scheduler;
import org.elasticsearch.watcher.scheduler.schedule.Schedule;
import org.elasticsearch.common.joda.time.DateTime;
import java.util.Collection;
/**
*
*/
public interface Scheduler {
/**
* Starts the scheduler and schedules the specified jobs before returning.
*/
void start(Collection<? extends Job> jobs);
/**
* Stops the scheduler.
*/
void stop();
/**
* Adds and schedules the give job
*/
void add(Job job);
/**
* Removes the scheduled job that is associated with the given name
*/
boolean remove(String jobName);
void addListener(Listener listener);
public static interface Listener {
void fire(String jobName, DateTime scheduledFireTime, DateTime fireTime);
}
public static interface Job {
String name();
Schedule schedule();
}
}

View File

@ -1,60 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.watcher.scheduler;
import org.elasticsearch.watcher.scheduler.schedule.*;
import org.elasticsearch.common.inject.AbstractModule;
import org.elasticsearch.common.inject.multibindings.MapBinder;
import java.util.HashMap;
import java.util.Map;
/**
*
*/
public class SchedulerModule extends AbstractModule {
private final Class<? extends Scheduler> schedulerClass;
private final Map<String, Class<? extends Schedule.Parser>> parsers = new HashMap<>();
public SchedulerModule() {
this(InternalScheduler.class);
}
protected SchedulerModule(Class<? extends Scheduler> schedulerClass) {
this.schedulerClass = schedulerClass;
}
@Override
protected void configure() {
MapBinder<String, Schedule.Parser> mbinder = MapBinder.newMapBinder(binder(), String.class, Schedule.Parser.class);
bind(IntervalSchedule.Parser.class).asEagerSingleton();
mbinder.addBinding(IntervalSchedule.TYPE).to(IntervalSchedule.Parser.class);
bind(CronSchedule.Parser.class).asEagerSingleton();
mbinder.addBinding(CronSchedule.TYPE).to(CronSchedule.Parser.class);
bind(HourlySchedule.Parser.class).asEagerSingleton();
mbinder.addBinding(HourlySchedule.TYPE).to(HourlySchedule.Parser.class);
bind(DailySchedule.Parser.class).asEagerSingleton();
mbinder.addBinding(DailySchedule.TYPE).to(DailySchedule.Parser.class);
bind(WeeklySchedule.Parser.class).asEagerSingleton();
mbinder.addBinding(WeeklySchedule.TYPE).to(WeeklySchedule.Parser.class);
bind(MonthlySchedule.Parser.class).asEagerSingleton();
mbinder.addBinding(MonthlySchedule.TYPE).to(MonthlySchedule.Parser.class);
bind(YearlySchedule.Parser.class).asEagerSingleton();
mbinder.addBinding(YearlySchedule.TYPE).to(YearlySchedule.Parser.class);
for (Map.Entry<String, Class<? extends Schedule.Parser>> entry : parsers.entrySet()) {
bind(entry.getValue()).asEagerSingleton();
mbinder.addBinding(entry.getKey()).to(entry.getValue());
}
bind(ScheduleRegistry.class).asEagerSingleton();
bind(schedulerClass).asEagerSingleton();
bind(Scheduler.class).to(schedulerClass);
}
}

View File

@ -5,6 +5,7 @@
*/
package org.elasticsearch.watcher.support;
import org.elasticsearch.watcher.trigger.TriggerEvent;
import org.elasticsearch.watcher.watch.WatchExecutionContext;
import org.elasticsearch.watcher.watch.Payload;
import org.elasticsearch.common.joda.time.DateTime;
@ -20,21 +21,21 @@ public final class Variables {
public static final String CTX = "ctx";
public static final String WATCH_NAME = "watch_name";
public static final String EXECUTION_TIME = "execution_time";
public static final String FIRE_TIME = "fire_time";
public static final String SCHEDULED_FIRE_TIME = "scheduled_fire_time";
public static final String TRIGGER = "trigger";
public static final String PAYLOAD = "payload";
public static Map<String, Object> createCtxModel(WatchExecutionContext ctx, Payload payload) {
return createCtxModel(ctx.watch().name(), ctx.executionTime(), ctx.fireTime(), ctx.scheduledTime(), payload);
return createCtxModel(ctx.watch().name(), ctx.executionTime(), ctx.triggerEvent(), payload);
}
public static Map<String, Object> createCtxModel(String watchName, DateTime executionTime, DateTime fireTime, DateTime scheduledTime, Payload payload) {
public static Map<String, Object> createCtxModel(String watchName, DateTime executionTime, TriggerEvent triggerEvent, Payload payload) {
Map<String, Object> vars = new HashMap<>();
vars.put(WATCH_NAME, watchName);
vars.put(EXECUTION_TIME, executionTime);
vars.put(FIRE_TIME, fireTime);
vars.put(SCHEDULED_FIRE_TIME, scheduledTime);
vars.put(PAYLOAD, payload.data());
vars.put(TRIGGER, triggerEvent.data());
if (payload != null) {
vars.put(PAYLOAD, payload.data());
}
Map<String, Object> model = new HashMap<>();
model.put(CTX, vars);
return model;

View File

@ -36,7 +36,7 @@ public class ScriptServiceProxy implements InitializingService.Initializable {
this.service = injector.getInstance(ScriptService.class);
}
public ExecutableScript executable(String lang, String script, ScriptService.ScriptType scriptType, Map vars) {
public ExecutableScript executable(String lang, String script, ScriptService.ScriptType scriptType, Map<String, Object> vars) {
return service.executable(lang, script, scriptType, vars);
}

View File

@ -0,0 +1,29 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.watcher.trigger;
import org.elasticsearch.common.component.AbstractComponent;
import org.elasticsearch.common.settings.Settings;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
/**
*
*/
public abstract class AbstractTriggerEngine<T extends Trigger, E extends TriggerEvent> extends AbstractComponent implements TriggerEngine<T, E> {
protected final List<Listener> listeners = new CopyOnWriteArrayList<>();
public AbstractTriggerEngine(Settings settings) {
super(settings);
}
@Override
public void register(Listener listener) {
listeners.add(listener);
}
}

View File

@ -0,0 +1,32 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.watcher.trigger;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentParser;
import java.io.IOException;
/**
*
*/
public interface Trigger extends ToXContent {
String type();
public static interface Parser<T extends Trigger> {
String type();
T parse(XContentParser parser) throws IOException;
}
public static interface SourceBuilder extends ToXContent {
String type();
}
}

View File

@ -0,0 +1,22 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.watcher.trigger;
import org.elasticsearch.watcher.trigger.schedule.Schedule;
import org.elasticsearch.watcher.trigger.schedule.ScheduleTrigger;
/**
*
*/
public final class TriggerBuilders {
private TriggerBuilders() {
}
public static ScheduleTrigger.SourceBuilder schedule(Schedule schedule) {
return new ScheduleTrigger.SourceBuilder(schedule);
}
}

View File

@ -0,0 +1,51 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.watcher.trigger;
import org.elasticsearch.common.xcontent.XContentParser;
import java.io.IOException;
import java.util.Collection;
/**
*
*/
public interface TriggerEngine<T extends Trigger, E extends TriggerEvent> {
String type();
/**
* It's the responsibility of the trigger engine implementation to select the appropriate jobs
* from the given list of jobs
*/
void start(Collection<Job> jobs);
void stop();
void register(Listener listener);
void add(Job job);
boolean remove(String jobName);
T parseTrigger(String context, XContentParser parser) throws IOException;
E parseTriggerEvent(String context, XContentParser parser) throws IOException;
public static interface Listener {
void triggered(String jobName, TriggerEvent event);
}
public static interface Job {
String name();
Trigger trigger();
}
}

View File

@ -0,0 +1,44 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.watcher.trigger;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.joda.time.DateTime;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.watcher.support.WatcherDateUtils;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
*
*/
public abstract class TriggerEvent implements ToXContent {
public static final ParseField TRIGGERED_TIME_FIELD = new ParseField("triggered_time");
protected final DateTime triggeredTime;
protected final Map<String, Object> data;
public TriggerEvent(DateTime triggeredTime) {
this.triggeredTime = triggeredTime;
this.data = new HashMap<>();
data.put(TRIGGERED_TIME_FIELD.getPreferredName(), triggeredTime);
}
public abstract String type();
public DateTime triggeredTime() {
return triggeredTime;
}
public final Map<String, Object> data() {
return data;
}
}

View File

@ -3,20 +3,20 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.watcher.scheduler;
package org.elasticsearch.watcher.trigger;
import org.elasticsearch.watcher.WatcherException;
/**
*
*/
public class SchedulerException extends WatcherException {
public class TriggerException extends WatcherException {
public SchedulerException(String msg) {
public TriggerException(String msg) {
super(msg);
}
public SchedulerException(String msg, Throwable cause) {
public TriggerException(String msg, Throwable cause) {
super(msg, cause);
}
}

View File

@ -0,0 +1,53 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.watcher.trigger;
import org.elasticsearch.common.collect.ImmutableSet;
import org.elasticsearch.common.inject.AbstractModule;
import org.elasticsearch.common.inject.Module;
import org.elasticsearch.common.inject.SpawnModules;
import org.elasticsearch.common.inject.multibindings.Multibinder;
import org.elasticsearch.watcher.trigger.schedule.ScheduleModule;
import java.util.HashSet;
import java.util.Set;
/**
*
*/
public class TriggerModule extends AbstractModule implements SpawnModules {
private final Set<Class<? extends TriggerEngine>> engines = new HashSet<>();
public TriggerModule() {
registerStandardEngines();
}
public void registerEngine(Class<? extends TriggerEngine> engineType) {
engines.add(engineType);
}
protected void registerStandardEngines() {
registerEngine(ScheduleModule.triggerEngineType());
}
@Override
public Iterable<? extends Module> spawnModules() {
return ImmutableSet.<Module>of(new ScheduleModule());
}
@Override
protected void configure() {
Multibinder<TriggerEngine> mbinder = Multibinder.newSetBinder(binder(), TriggerEngine.class);
for (Class<? extends TriggerEngine> engine : engines) {
bind(engine).asEagerSingleton();
mbinder.addBinding().to(engine);
}
bind(TriggerService.class).asEagerSingleton();
}
}

View File

@ -0,0 +1,134 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.watcher.trigger;
import org.elasticsearch.common.collect.ImmutableMap;
import org.elasticsearch.common.component.AbstractComponent;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.watcher.WatcherSettingsException;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
/**
*
*/
public class TriggerService extends AbstractComponent {
private final Listeners listeners;
private final ImmutableMap<String, TriggerEngine> engines;
@Inject
public TriggerService(Settings settings, Set<TriggerEngine> engines) {
super(settings);
listeners = new Listeners();
ImmutableMap.Builder<String, TriggerEngine> builder = ImmutableMap.builder();
for (TriggerEngine engine : engines) {
builder.put(engine.type(), engine);
engine.register(listeners);
}
this.engines = builder.build();
}
public synchronized void start(Collection<? extends TriggerEngine.Job> jobs) {
for (TriggerEngine engine : engines.values()) {
engine.start(jobs);
}
}
public synchronized void stop() {
for (TriggerEngine engine : engines.values()) {
engine.stop();
}
}
public void add(TriggerEngine.Job job) {
engines.get(job.trigger().type()).add(job);
}
public boolean remove(String jobName) {
for (TriggerEngine engine : engines.values()) {
if (engine.remove(jobName)) {
return true;
}
}
return false;
}
public void register(TriggerEngine.Listener listener) {
listeners.add(listener);
}
public Trigger parseTrigger(String jobName, XContentParser parser) throws IOException {
XContentParser.Token token = parser.currentToken();
assert token == XContentParser.Token.START_OBJECT;
token = parser.nextToken();
if (token != XContentParser.Token.FIELD_NAME) {
throw new WatcherSettingsException("could not parse trigger for [" + jobName + "]. expected trigger type string field, but found [" + token + "]");
}
String type = parser.text();
token = parser.nextToken();
if (token != XContentParser.Token.START_OBJECT) {
throw new WatcherSettingsException("could not parse trigger [" + type + "] for [" + jobName + "]. expected trigger an object as the trigger body, but found [" + token + "]");
}
Trigger trigger = parseTrigger(jobName, type, parser);
token = parser.nextToken();
assert token == XContentParser.Token.END_OBJECT;
return trigger;
}
public Trigger parseTrigger(String jobName, String type, XContentParser parser) throws IOException {
TriggerEngine engine = engines.get(type);
assert engine != null;
return engine.parseTrigger(jobName, parser);
}
public TriggerEvent parseTriggerEvent(String historyRecordId, XContentParser parser) throws IOException {
XContentParser.Token token = parser.currentToken();
assert token == XContentParser.Token.START_OBJECT;
token = parser.nextToken();
if (token != XContentParser.Token.FIELD_NAME) {
throw new TriggerException("could not parse trigger event for [" + historyRecordId + "]. expected trigger type string field, but found [" + token + "]");
}
String type = parser.text();
token = parser.nextToken();
if (token != XContentParser.Token.START_OBJECT) {
throw new WatcherSettingsException("could not parse trigger event [" + type + "] for [" + historyRecordId + "]. expected trigger an object as the trigger body, but found [" + token + "]");
}
TriggerEvent trigger = parseTriggerEvent(historyRecordId, type, parser);
token = parser.nextToken();
assert token == XContentParser.Token.END_OBJECT;
return trigger;
}
public TriggerEvent parseTriggerEvent(String context, String type, XContentParser parser) throws IOException {
TriggerEngine engine = engines.get(type);
assert engine != null;
return engine.parseTriggerEvent(context, parser);
}
static class Listeners implements TriggerEngine.Listener {
private List<TriggerEngine.Listener> listeners = new CopyOnWriteArrayList<>();
public void add(TriggerEngine.Listener listener) {
listeners.add(listener);
}
@Override
public void triggered(String jobName, TriggerEvent event) {
for (TriggerEngine.Listener listener : listeners) {
listener.triggered(jobName, event);
}
}
}
}

View File

@ -3,11 +3,11 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.watcher.scheduler.schedule;
package org.elasticsearch.watcher.trigger.schedule;
import org.elasticsearch.watcher.WatcherSettingsException;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.watcher.WatcherSettingsException;
import org.quartz.CronExpression;
import java.io.IOException;

View File

@ -3,7 +3,7 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.watcher.scheduler.schedule;
package org.elasticsearch.watcher.trigger.schedule;
import java.util.Arrays;
import java.util.Objects;

View File

@ -3,13 +3,13 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.watcher.scheduler.schedule;
package org.elasticsearch.watcher.trigger.schedule;
import org.elasticsearch.watcher.WatcherSettingsException;
import org.elasticsearch.watcher.scheduler.schedule.support.DayTimes;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.watcher.WatcherSettingsException;
import org.elasticsearch.watcher.trigger.schedule.support.DayTimes;
import java.io.IOException;
import java.util.ArrayList;

View File

@ -3,14 +3,14 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.watcher.scheduler.schedule;
package org.elasticsearch.watcher.trigger.schedule;
import org.elasticsearch.watcher.WatcherSettingsException;
import org.elasticsearch.watcher.scheduler.schedule.support.DayTimes;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.primitives.Ints;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.watcher.WatcherSettingsException;
import org.elasticsearch.watcher.trigger.schedule.support.DayTimes;
import java.io.IOException;
import java.util.ArrayList;

View File

@ -3,12 +3,12 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.watcher.scheduler.schedule;
package org.elasticsearch.watcher.trigger.schedule;
import org.elasticsearch.watcher.WatcherSettingsException;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.watcher.WatcherSettingsException;
import java.io.IOException;
import java.util.Locale;

View File

@ -3,12 +3,12 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.watcher.scheduler.schedule;
package org.elasticsearch.watcher.trigger.schedule;
import org.elasticsearch.watcher.WatcherSettingsException;
import org.elasticsearch.watcher.scheduler.schedule.support.MonthTimes;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.watcher.trigger.schedule.support.MonthTimes;
import java.io.IOException;
import java.util.ArrayList;

View File

@ -3,7 +3,7 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.watcher.scheduler.schedule;
package org.elasticsearch.watcher.trigger.schedule;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentParser;
@ -17,11 +17,10 @@ public interface Schedule extends ToXContent {
String type();
static interface Parser<S extends Schedule> {
public static interface Parser<S extends Schedule> {
String type();
S parse(XContentParser parser) throws IOException;
}
}

View File

@ -0,0 +1,53 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.watcher.trigger.schedule;
import org.elasticsearch.common.inject.AbstractModule;
import org.elasticsearch.common.inject.multibindings.MapBinder;
import org.elasticsearch.watcher.trigger.TriggerEngine;
import org.elasticsearch.watcher.trigger.schedule.quartz.QuartzScheduleTriggerEngine;
import java.util.HashMap;
import java.util.Map;
/**
*
*/
public class ScheduleModule extends AbstractModule {
private final Map<String, Class<? extends Schedule.Parser>> parsers = new HashMap<>();
public ScheduleModule() {
registerScheduleParser(CronSchedule.TYPE, CronSchedule.Parser.class);
registerScheduleParser(DailySchedule.TYPE, DailySchedule.Parser.class);
registerScheduleParser(HourlySchedule.TYPE, HourlySchedule.Parser.class);
registerScheduleParser(IntervalSchedule.TYPE, IntervalSchedule.Parser.class);
registerScheduleParser(MonthlySchedule.TYPE, MonthlySchedule.Parser.class);
registerScheduleParser(WeeklySchedule.TYPE, WeeklySchedule.Parser.class);
registerScheduleParser(YearlySchedule.TYPE, YearlySchedule.Parser.class);
}
public static Class<? extends TriggerEngine> triggerEngineType() {
return QuartzScheduleTriggerEngine.class;
}
public void registerScheduleParser(String parserType, Class<? extends Schedule.Parser> parserClass) {
parsers.put(parserType, parserClass);
}
@Override
protected void configure() {
MapBinder<String, Schedule.Parser> mbinder = MapBinder.newMapBinder(binder(), String.class, Schedule.Parser.class);
for (Map.Entry<String, Class<? extends Schedule.Parser>> entry : parsers.entrySet()) {
bind(entry.getValue()).asEagerSingleton();
mbinder.addBinding(entry.getKey()).to(entry.getValue());
}
bind(ScheduleRegistry.class).asEagerSingleton();
}
}

View File

@ -3,7 +3,7 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.watcher.scheduler.schedule;
package org.elasticsearch.watcher.trigger.schedule;
import org.elasticsearch.watcher.WatcherSettingsException;
import org.elasticsearch.common.collect.ImmutableMap;
@ -30,7 +30,7 @@ public class ScheduleRegistry {
return parsers.keySet();
}
public Schedule parse(XContentParser parser) throws IOException {
public Schedule parse(String context, XContentParser parser) throws IOException {
String type = null;
XContentParser.Token token;
Schedule schedule = null;
@ -38,7 +38,7 @@ public class ScheduleRegistry {
if (token == XContentParser.Token.FIELD_NAME) {
type = parser.currentName();
} else if (type != null) {
schedule = parse(type, parser);
schedule = parse(context, type, parser);
} else {
throw new WatcherSettingsException("could not parse schedule. expected a schedule type field, but found [" + token + "]");
}
@ -49,10 +49,10 @@ public class ScheduleRegistry {
return schedule;
}
public Schedule parse(String type, XContentParser parser) throws IOException {
public Schedule parse(String context, String type, XContentParser parser) throws IOException {
Schedule.Parser scheduleParser = parsers.get(type);
if (scheduleParser == null) {
throw new WatcherSettingsException("could not parse schedule. unknown schedule type [" + type + "]");
throw new WatcherSettingsException("could not parse schedule for [" + context + "]. unknown schedule type [" + type + "]");
}
return scheduleParser.parse(parser);
}

View File

@ -0,0 +1,78 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.watcher.trigger.schedule;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.watcher.trigger.Trigger;
import java.io.IOException;
/**
*
*/
public class ScheduleTrigger implements Trigger {
public static final String TYPE = "schedule";
private final Schedule schedule;
public ScheduleTrigger(Schedule schedule) {
this.schedule = schedule;
}
@Override
public String type() {
return TYPE;
}
public Schedule schedule() {
return schedule;
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
return builder.startObject().field(schedule.type(), schedule).endObject();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ScheduleTrigger trigger = (ScheduleTrigger) o;
if (!schedule.equals(trigger.schedule)) return false;
return true;
}
@Override
public int hashCode() {
return schedule.hashCode();
}
public static class SourceBuilder implements Trigger.SourceBuilder {
private final Schedule schedule;
public SourceBuilder(Schedule schedule) {
this.schedule = schedule;
}
@Override
public String type() {
return TYPE;
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
return builder.startObject()
.field(schedule.type(), schedule)
.endObject();
}
}
}

View File

@ -0,0 +1,26 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.watcher.trigger.schedule;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.watcher.trigger.AbstractTriggerEngine;
/**
*
*/
public abstract class ScheduleTriggerEngine extends AbstractTriggerEngine<ScheduleTrigger, ScheduleTriggerEvent> {
public static final String TYPE = ScheduleTrigger.TYPE;
public ScheduleTriggerEngine(Settings settings) {
super(settings);
}
@Override
public String type() {
return TYPE;
}
}

View File

@ -0,0 +1,89 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.watcher.trigger.schedule;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.joda.time.DateTime;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.shield.ShieldException;
import org.elasticsearch.watcher.support.WatcherDateUtils;
import org.elasticsearch.watcher.trigger.TriggerEvent;
import java.io.IOException;
/**
*
*/
public class ScheduleTriggerEvent extends TriggerEvent {
public static final ParseField SCHEDULED_TIME_FIELD = new ParseField("scheduled_time");
private final DateTime scheduledTime;
public ScheduleTriggerEvent(DateTime triggeredTime, DateTime scheduledTime) {
super(triggeredTime);
this.scheduledTime = scheduledTime;
data.put(SCHEDULED_TIME_FIELD.getPreferredName(), scheduledTime);
}
@Override
public String type() {
return ScheduleTrigger.TYPE;
}
public DateTime scheduledTime() {
return scheduledTime;
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
return builder.startObject()
.field(TRIGGERED_TIME_FIELD.getPreferredName(), WatcherDateUtils.formatDate(triggeredTime))
.field(SCHEDULED_TIME_FIELD.getPreferredName(), WatcherDateUtils.formatDate(scheduledTime))
.endObject();
}
public static ScheduleTriggerEvent parse(String context, XContentParser parser) throws IOException {
DateTime triggeredTime = null;
DateTime scheduledTime = null;
String currentFieldName = null;
XContentParser.Token token = null;
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
if (token == XContentParser.Token.FIELD_NAME) {
currentFieldName = parser.currentName();
} else {
if (token == XContentParser.Token.VALUE_STRING) {
if (TRIGGERED_TIME_FIELD.match(currentFieldName)) {
triggeredTime = WatcherDateUtils.parseDate(parser.text());
} else if (SCHEDULED_TIME_FIELD.match(currentFieldName)) {
scheduledTime = WatcherDateUtils.parseDate(parser.text());
} else {
throw new ParseException("could not parse trigger event for [" + context + "]. unknown string value field [" + currentFieldName + "]");
}
} else {
throw new ParseException("could not parse trigger event for [" + context + "]. unexpected token [" + token + "]");
}
}
}
// should never be, it's fully controlled internally (not coming from the user)
assert triggeredTime != null && scheduledTime != null;
return new ScheduleTriggerEvent(triggeredTime, scheduledTime);
}
public static class ParseException extends ShieldException {
public ParseException(String msg) {
super(msg);
}
public ParseException(String msg, Throwable cause) {
super(msg, cause);
}
}
}

View File

@ -3,7 +3,7 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.watcher.scheduler.schedule;
package org.elasticsearch.watcher.trigger.schedule;
/**
* A static factory for all available schedules.
@ -46,7 +46,7 @@ public class Schedules {
*
* @param cronExpressions one or more cron expressions
* @return the newly created cron schedule.
* @throws org.elasticsearch.watcher.scheduler.schedule.CronSchedule.ValidationException if any of the given expression is invalid
* @throws CronSchedule.ValidationException if any of the given expression is invalid
*/
public static CronSchedule cron(String... cronExpressions) {
return new CronSchedule(cronExpressions);

View File

@ -3,12 +3,12 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.watcher.scheduler.schedule;
package org.elasticsearch.watcher.trigger.schedule;
import org.elasticsearch.watcher.WatcherSettingsException;
import org.elasticsearch.watcher.scheduler.schedule.support.WeekTimes;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.watcher.WatcherSettingsException;
import org.elasticsearch.watcher.trigger.schedule.support.WeekTimes;
import java.io.IOException;
import java.util.ArrayList;

View File

@ -3,12 +3,12 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.watcher.scheduler.schedule;
package org.elasticsearch.watcher.trigger.schedule;
import org.elasticsearch.watcher.WatcherSettingsException;
import org.elasticsearch.watcher.scheduler.schedule.support.YearTimes;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.watcher.WatcherSettingsException;
import org.elasticsearch.watcher.trigger.schedule.support.YearTimes;
import java.io.IOException;
import java.util.ArrayList;

View File

@ -3,31 +3,35 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.watcher.scheduler;
package org.elasticsearch.watcher.trigger.schedule.quartz;
import org.elasticsearch.watcher.WatcherPlugin;
import org.elasticsearch.watcher.WatcherSettingsException;
import org.elasticsearch.watcher.scheduler.schedule.CronnableSchedule;
import org.elasticsearch.watcher.scheduler.schedule.IntervalSchedule;
import org.elasticsearch.watcher.scheduler.schedule.Schedule;
import org.elasticsearch.watcher.support.clock.Clock;
import org.elasticsearch.common.component.AbstractComponent;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.joda.time.DateTime;
import org.elasticsearch.common.joda.time.DateTimeZone;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.concurrent.EsThreadPoolExecutor;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.watcher.WatcherPlugin;
import org.elasticsearch.watcher.WatcherSettingsException;
import org.elasticsearch.watcher.support.clock.Clock;
import org.elasticsearch.watcher.trigger.TriggerException;
import org.elasticsearch.watcher.trigger.schedule.*;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import org.quartz.simpl.SimpleJobFactory;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import static org.elasticsearch.watcher.scheduler.WatcherQuartzJob.jobDetail;
import static org.elasticsearch.watcher.trigger.schedule.quartz.WatcherQuartzJob.jobDetail;
public class InternalScheduler extends AbstractComponent implements Scheduler {
/**
*
*/
public class QuartzScheduleTriggerEngine extends ScheduleTriggerEngine {
private final ScheduleRegistry scheduleRegistry;
// Not happy about it, but otherwise we're stuck with Quartz's SimpleThreadPool
private volatile static ThreadPool threadPool;
@ -36,15 +40,13 @@ public class InternalScheduler extends AbstractComponent implements Scheduler {
private final DateTimeZone defaultTimeZone;
private volatile org.quartz.Scheduler scheduler;
private List<Listener> listeners;
@Inject
public InternalScheduler(Settings settings, ThreadPool threadPool, Clock clock) {
public QuartzScheduleTriggerEngine(Settings settings, ScheduleRegistry scheduleRegistry, ThreadPool threadPool, Clock clock) {
super(settings);
InternalScheduler.threadPool = threadPool;
this.scheduleRegistry = scheduleRegistry;
QuartzScheduleTriggerEngine.threadPool = threadPool;
this.clock = clock;
this.listeners = new CopyOnWriteArrayList<>();
String timeZoneStr = componentSettings.get("time_zone", "UTC");
try {
this.defaultTimeZone = DateTimeZone.forID(timeZoneStr);
@ -54,7 +56,12 @@ public class InternalScheduler extends AbstractComponent implements Scheduler {
}
@Override
public synchronized void start(Collection<? extends Job> jobs) {
public String type() {
return ScheduleTrigger.TYPE;
}
@Override
public void start(Collection<Job> jobs) {
try {
logger.info("Starting scheduler");
// Can't start a scheduler that has been shutdown, so we need to re-create each time start() is invoked
@ -68,7 +75,10 @@ public class InternalScheduler extends AbstractComponent implements Scheduler {
scheduler.setJobFactory(new SimpleJobFactory());
Map<JobDetail, Set<? extends Trigger>> quartzJobs = new HashMap<>();
for (Job job : jobs) {
quartzJobs.put(jobDetail(job.name(), this), createTrigger(job.schedule(), defaultTimeZone, clock));
if (job.trigger() instanceof ScheduleTrigger) {
ScheduleTrigger trigger = (ScheduleTrigger) job.trigger();
quartzJobs.put(jobDetail(job.name(), this), createTrigger(trigger.schedule(), defaultTimeZone, clock));
}
}
scheduler.scheduleJobs(quartzJobs, false);
scheduler.start();
@ -77,7 +87,8 @@ public class InternalScheduler extends AbstractComponent implements Scheduler {
}
}
public synchronized void stop() {
@Override
public void stop() {
try {
org.quartz.Scheduler scheduler = this.scheduler;
if (scheduler != null) {
@ -92,39 +103,28 @@ public class InternalScheduler extends AbstractComponent implements Scheduler {
}
@Override
public void addListener(Listener listener) {
listeners.add(listener);
}
void notifyListeners(String name, JobExecutionContext ctx) {
DateTime scheduledTime = new DateTime(ctx.getScheduledFireTime());
DateTime fireTime = new DateTime(ctx.getFireTime());
for (Listener listener : listeners) {
listener.fire(name, scheduledTime, fireTime);
}
}
/**
* Schedules the given job
*/
public void add(Job job) {
assert job.trigger() instanceof ScheduleTrigger;
ScheduleTrigger trigger = (ScheduleTrigger) job.trigger();
try {
logger.trace("scheduling [{}] with schedule [{}]", job.name(), job.schedule());
scheduler.scheduleJob(jobDetail(job.name(), this), createTrigger(job.schedule(), defaultTimeZone, clock), true);
logger.trace("scheduling [{}] with schedule [{}]", job.name(), trigger.schedule());
scheduler.scheduleJob(jobDetail(job.name(), this), createTrigger(trigger.schedule(), defaultTimeZone, clock), true);
} catch (org.quartz.SchedulerException se) {
logger.error("Failed to schedule job",se);
throw new SchedulerException("Failed to schedule job", se);
logger.error("failed to schedule job",se);
throw new TriggerException("failed to schedule job", se);
}
}
@Override
public boolean remove(String jobName) {
try {
return scheduler.deleteJob(new JobKey(jobName));
} catch (org.quartz.SchedulerException se){
throw new SchedulerException("Failed to remove [" + jobName + "] from the scheduler", se);
throw new TriggerException("failed to remove [" + jobName + "] from the scheduler", se);
}
}
static Set<Trigger> createTrigger(Schedule schedule, DateTimeZone timeZone, Clock clock) {
HashSet<Trigger> triggers = new HashSet<>();
if (schedule instanceof CronnableSchedule) {
@ -147,6 +147,25 @@ public class InternalScheduler extends AbstractComponent implements Scheduler {
}
@Override
public ScheduleTrigger parseTrigger(String context, XContentParser parser) throws IOException {
Schedule schedule = scheduleRegistry.parse(context, parser);
return new ScheduleTrigger(schedule);
}
@Override
public ScheduleTriggerEvent parseTriggerEvent(String context, XContentParser parser) throws IOException {
return ScheduleTriggerEvent.parse(context, parser);
}
void notifyListeners(String name, JobExecutionContext ctx) {
ScheduleTriggerEvent event = new ScheduleTriggerEvent(new DateTime(ctx.getFireTime()), new DateTime(ctx.getScheduledFireTime()));
for (Listener listener : listeners) {
listener.triggered(name, event);
}
}
// This Quartz thread pool will always accept. On this thread we will only index a watch record and add it to the work queue
public static final class WatcherQuartzThreadPool implements org.quartz.spi.ThreadPool {
@ -190,5 +209,4 @@ public class InternalScheduler extends AbstractComponent implements Scheduler {
public void setInstanceName(String schedName) {
}
}
}

View File

@ -3,13 +3,13 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.watcher.scheduler;
package org.elasticsearch.watcher.trigger.schedule.quartz;
import org.quartz.*;
public class WatcherQuartzJob implements Job {
static final String SCHEDULER_KEY = "scheduler";
static final String ENGINE_KEY = "engine";
public WatcherQuartzJob() {
}
@ -17,7 +17,7 @@ public class WatcherQuartzJob implements Job {
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
String watchName = jobExecutionContext.getJobDetail().getKey().getName();
InternalScheduler scheduler = (InternalScheduler) jobExecutionContext.getJobDetail().getJobDataMap().get(SCHEDULER_KEY);
QuartzScheduleTriggerEngine scheduler = (QuartzScheduleTriggerEngine) jobExecutionContext.getJobDetail().getJobDataMap().get(ENGINE_KEY);
scheduler.notifyListeners(watchName, jobExecutionContext);
}
@ -25,9 +25,9 @@ public class WatcherQuartzJob implements Job {
return new JobKey(watchName);
}
static JobDetail jobDetail(String watchName, InternalScheduler scheduler) {
static JobDetail jobDetail(String watchName, QuartzScheduleTriggerEngine engine) {
JobDetail job = JobBuilder.newJob(WatcherQuartzJob.class).withIdentity(watchName).build();
job.getJobDataMap().put("scheduler", scheduler);
job.getJobDataMap().put(ENGINE_KEY, engine);
return job;
}
}

View File

@ -3,7 +3,7 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.watcher.scheduler.schedule.support;
package org.elasticsearch.watcher.trigger.schedule.support;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;

View File

@ -3,13 +3,13 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.watcher.scheduler.schedule.support;
package org.elasticsearch.watcher.trigger.schedule.support;
import org.elasticsearch.watcher.WatcherException;
import org.elasticsearch.watcher.WatcherSettingsException;
import org.elasticsearch.common.primitives.Ints;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.watcher.WatcherException;
import org.elasticsearch.watcher.WatcherSettingsException;
import java.io.IOException;
import java.util.ArrayList;

View File

@ -3,7 +3,7 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.watcher.scheduler.schedule.support;
package org.elasticsearch.watcher.trigger.schedule.support;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;

View File

@ -3,15 +3,15 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.watcher.scheduler.schedule.support;
package org.elasticsearch.watcher.trigger.schedule.support;
import org.elasticsearch.watcher.WatcherException;
import org.elasticsearch.watcher.WatcherSettingsException;
import org.elasticsearch.common.base.Joiner;
import org.elasticsearch.common.collect.ImmutableSet;
import org.elasticsearch.common.primitives.Ints;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.watcher.WatcherException;
import org.elasticsearch.watcher.WatcherSettingsException;
import java.io.IOException;
import java.util.Arrays;

View File

@ -3,7 +3,7 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.watcher.scheduler.schedule.support;
package org.elasticsearch.watcher.trigger.schedule.support;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.xcontent.ToXContent;

View File

@ -3,13 +3,13 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.watcher.scheduler.schedule.support;
package org.elasticsearch.watcher.trigger.schedule.support;
import org.elasticsearch.watcher.WatcherException;
import org.elasticsearch.common.collect.ImmutableSet;
import org.elasticsearch.common.primitives.Ints;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.watcher.WatcherException;
import java.io.IOException;
import java.util.*;

View File

@ -3,15 +3,15 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.watcher.scheduler.schedule.support;
package org.elasticsearch.watcher.trigger.schedule.support;
import org.elasticsearch.watcher.WatcherException;
import org.elasticsearch.watcher.WatcherSettingsException;
import org.elasticsearch.common.base.Joiner;
import org.elasticsearch.common.collect.ImmutableSet;
import org.elasticsearch.common.primitives.Ints;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.watcher.WatcherException;
import org.elasticsearch.watcher.WatcherSettingsException;
import java.io.IOException;
import java.util.*;

View File

@ -5,24 +5,6 @@
*/
package org.elasticsearch.watcher.watch;
import org.elasticsearch.watcher.WatcherException;
import org.elasticsearch.watcher.WatcherSettingsException;
import org.elasticsearch.watcher.actions.ActionRegistry;
import org.elasticsearch.watcher.actions.Actions;
import org.elasticsearch.watcher.condition.Condition;
import org.elasticsearch.watcher.condition.ConditionRegistry;
import org.elasticsearch.watcher.condition.simple.AlwaysTrueCondition;
import org.elasticsearch.watcher.input.Input;
import org.elasticsearch.watcher.input.InputRegistry;
import org.elasticsearch.watcher.input.NoneInput;
import org.elasticsearch.watcher.scheduler.Scheduler;
import org.elasticsearch.watcher.scheduler.schedule.Schedule;
import org.elasticsearch.watcher.scheduler.schedule.ScheduleRegistry;
import org.elasticsearch.watcher.support.clock.Clock;
import org.elasticsearch.watcher.throttle.WatchThrottler;
import org.elasticsearch.watcher.throttle.Throttler;
import org.elasticsearch.watcher.transform.Transform;
import org.elasticsearch.watcher.transform.TransformRegistry;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.bytes.BytesReference;
@ -39,6 +21,24 @@ import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentHelper;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.watcher.WatcherException;
import org.elasticsearch.watcher.WatcherSettingsException;
import org.elasticsearch.watcher.actions.ActionRegistry;
import org.elasticsearch.watcher.actions.Actions;
import org.elasticsearch.watcher.condition.Condition;
import org.elasticsearch.watcher.condition.ConditionRegistry;
import org.elasticsearch.watcher.condition.simple.AlwaysTrueCondition;
import org.elasticsearch.watcher.input.Input;
import org.elasticsearch.watcher.input.InputRegistry;
import org.elasticsearch.watcher.input.NoneInput;
import org.elasticsearch.watcher.support.clock.Clock;
import org.elasticsearch.watcher.throttle.Throttler;
import org.elasticsearch.watcher.throttle.WatchThrottler;
import org.elasticsearch.watcher.transform.Transform;
import org.elasticsearch.watcher.transform.TransformRegistry;
import org.elasticsearch.watcher.trigger.Trigger;
import org.elasticsearch.watcher.trigger.TriggerEngine;
import org.elasticsearch.watcher.trigger.TriggerService;
import java.io.IOException;
import java.util.Locale;
@ -46,10 +46,10 @@ import java.util.Map;
import static org.elasticsearch.watcher.support.WatcherDateUtils.*;
public class Watch implements Scheduler.Job, ToXContent {
public class Watch implements TriggerEngine.Job, ToXContent {
private final String name;
private final Schedule schedule;
private final Trigger trigger;
private final Input input;
private final Condition condition;
private final Actions actions;
@ -63,9 +63,9 @@ public class Watch implements Scheduler.Job, ToXContent {
@Nullable
private final Transform transform;
public Watch(String name, Clock clock, Schedule schedule, Input input, Condition condition, @Nullable Transform transform, Actions actions, @Nullable Map<String, Object> metadata, @Nullable TimeValue throttlePeriod, @Nullable Status status) {
public Watch(String name, Clock clock, Trigger trigger, Input input, Condition condition, @Nullable Transform transform, Actions actions, @Nullable Map<String, Object> metadata, @Nullable TimeValue throttlePeriod, @Nullable Status status) {
this.name = name;
this.schedule = schedule;
this.trigger = trigger;
this.input = input;
this.condition = condition;
this.actions = actions;
@ -80,8 +80,8 @@ public class Watch implements Scheduler.Job, ToXContent {
return name;
}
public Schedule schedule() {
return schedule;
public Trigger trigger() {
return trigger;
}
public Input input() { return input;}
@ -144,7 +144,7 @@ public class Watch implements Scheduler.Job, ToXContent {
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject();
builder.field(Parser.SCHEDULE_FIELD.getPreferredName()).startObject().field(schedule.type(), schedule).endObject();
builder.field(Parser.TRIGGER_FIELD.getPreferredName()).startObject().field(trigger.type(), trigger).endObject();
builder.field(Parser.INPUT_FIELD.getPreferredName()).startObject().field(input.type(), input).endObject();
builder.field(Parser.CONDITION_FIELD.getPreferredName()).startObject().field(condition.type(), condition).endObject();
if (transform != null) {
@ -164,7 +164,7 @@ public class Watch implements Scheduler.Job, ToXContent {
public static class Parser extends AbstractComponent {
public static final ParseField SCHEDULE_FIELD = new ParseField("schedule");
public static final ParseField TRIGGER_FIELD = new ParseField("trigger");
public static final ParseField INPUT_FIELD = new ParseField("input");
public static final ParseField CONDITION_FIELD = new ParseField("condition");
public static final ParseField ACTIONS_FIELD = new ParseField("actions");
@ -174,7 +174,7 @@ public class Watch implements Scheduler.Job, ToXContent {
public static final ParseField THROTTLE_PERIOD_FIELD = new ParseField("throttle_period");
private final ConditionRegistry conditionRegistry;
private final ScheduleRegistry scheduleRegistry;
private final TriggerService triggerService;
private final TransformRegistry transformRegistry;
private final ActionRegistry actionRegistry;
private final InputRegistry inputRegistry;
@ -184,14 +184,14 @@ public class Watch implements Scheduler.Job, ToXContent {
private final Condition defaultCondition;
@Inject
public Parser(Settings settings, ConditionRegistry conditionRegistry, ScheduleRegistry scheduleRegistry,
public Parser(Settings settings, ConditionRegistry conditionRegistry, TriggerService triggerService,
TransformRegistry transformRegistry, ActionRegistry actionRegistry,
InputRegistry inputRegistry, Clock clock) {
super(settings);
this.conditionRegistry = conditionRegistry;
this.scheduleRegistry = scheduleRegistry;
this.transformRegistry = transformRegistry;
this.triggerService = triggerService;
this.actionRegistry = actionRegistry;
this.inputRegistry = inputRegistry;
this.clock = clock;
@ -212,7 +212,7 @@ public class Watch implements Scheduler.Job, ToXContent {
}
public Watch parse(String name, boolean includeStatus, XContentParser parser) throws IOException {
Schedule schedule = null;
Trigger trigger = null;
Input input = defaultInput;
Condition condition = defaultCondition;
Actions actions = null;
@ -229,8 +229,8 @@ public class Watch implements Scheduler.Job, ToXContent {
} else if (token == null ){
throw new WatcherException("could not parse watch [" + name + "]. null token");
} else if ((token.isValue() || token == XContentParser.Token.START_OBJECT || token == XContentParser.Token.START_ARRAY) && currentFieldName !=null ) {
if (SCHEDULE_FIELD.match(currentFieldName)) {
schedule = scheduleRegistry.parse(parser);
if (TRIGGER_FIELD.match(currentFieldName)) {
trigger = triggerService.parseTrigger(name, parser);
} else if (INPUT_FIELD.match(currentFieldName)) {
input = inputRegistry.parse(parser);
} else if (CONDITION_FIELD.match(currentFieldName)) {
@ -254,14 +254,14 @@ public class Watch implements Scheduler.Job, ToXContent {
}
}
}
if (schedule == null) {
throw new WatcherSettingsException("could not parse watch [" + name + "]. missing watch schedule");
if (trigger == null) {
throw new WatcherSettingsException("could not parse watch [" + name + "]. missing watch trigger");
}
if (actions == null) {
throw new WatcherSettingsException("could not parse watch [" + name + "]. missing watch actions");
}
return new Watch(name, clock, schedule, input, condition, transform, actions, metatdata, throttlePeriod, status);
return new Watch(name, clock, trigger, input, condition, transform, actions, metatdata, throttlePeriod, status);
}
}

View File

@ -5,12 +5,13 @@
*/
package org.elasticsearch.watcher.watch;
import org.elasticsearch.common.joda.time.DateTime;
import org.elasticsearch.watcher.actions.Action;
import org.elasticsearch.watcher.condition.Condition;
import org.elasticsearch.watcher.input.Input;
import org.elasticsearch.watcher.throttle.Throttler;
import org.elasticsearch.watcher.transform.Transform;
import org.elasticsearch.common.joda.time.DateTime;
import org.elasticsearch.watcher.trigger.TriggerEvent;
import java.util.HashMap;
import java.util.Map;
@ -23,8 +24,7 @@ public class WatchExecutionContext {
private final String id;
private final Watch watch;
private final DateTime executionTime;
private final DateTime fireTime;
private final DateTime scheduledTime;
private final TriggerEvent triggerEvent;
private Input.Result inputResult;
private Condition.Result conditionResult;
@ -34,12 +34,11 @@ public class WatchExecutionContext {
private Payload payload;
public WatchExecutionContext(String id, Watch watch, DateTime executionTime, DateTime fireTime, DateTime scheduledTime) {
public WatchExecutionContext(String id, Watch watch, DateTime executionTime, TriggerEvent triggerEvent) {
this.id = id;
this.watch = watch;
this.executionTime = executionTime;
this.fireTime = fireTime;
this.scheduledTime = scheduledTime;
this.triggerEvent = triggerEvent;
}
public String id() {
@ -54,12 +53,8 @@ public class WatchExecutionContext {
return executionTime;
}
public DateTime fireTime() {
return fireTime;
}
public DateTime scheduledTime() {
return scheduledTime;
public TriggerEvent triggerEvent() {
return triggerEvent;
}
public Payload payload() {

View File

@ -10,13 +10,13 @@ import org.elasticsearch.ElasticsearchIllegalStateException;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.watcher.WatcherException;
import org.elasticsearch.watcher.history.HistoryService;
import org.elasticsearch.watcher.scheduler.Scheduler;
import org.elasticsearch.watcher.support.Callback;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.component.AbstractComponent;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.watcher.trigger.TriggerService;
import java.io.IOException;
import java.util.concurrent.ExecutionException;
@ -24,17 +24,17 @@ import java.util.concurrent.atomic.AtomicReference;
public class WatchService extends AbstractComponent {
private final Scheduler scheduler;
private final TriggerService triggerService;
private final WatchStore watchStore;
private final WatchLockService watchLockService;
private final HistoryService historyService;
private final AtomicReference<State> state = new AtomicReference<>(State.STOPPED);
@Inject
public WatchService(Settings settings, Scheduler scheduler, WatchStore watchStore, HistoryService historyService,
public WatchService(Settings settings, TriggerService triggerService, WatchStore watchStore, HistoryService historyService,
WatchLockService watchLockService) {
super(settings);
this.scheduler = scheduler;
this.triggerService = triggerService;
this.watchStore = watchStore;
this.watchLockService = watchLockService;
this.historyService = historyService;
@ -54,7 +54,7 @@ public class WatchService extends AbstractComponent {
@Override
public void onSuccess(ClusterState clusterState) {
scheduler.start(watchStore.watches().values());
triggerService.start(watchStore.watches().values());
state.set(State.STARTED);
logger.info("watch service has started");
}
@ -79,7 +79,7 @@ public class WatchService extends AbstractComponent {
logger.info("stopping watch service...");
watchLockService.stop();
historyService.stop();
scheduler.stop();
triggerService.stop();
watchStore.stop();
state.set(State.STOPPED);
logger.info("watch service has stopped");
@ -92,7 +92,7 @@ public class WatchService extends AbstractComponent {
try {
WatchStore.WatchDelete delete = watchStore.delete(name);
if (delete.deleteResponse().isFound()) {
scheduler.remove(name);
triggerService.remove(name);
}
return delete;
} finally {
@ -105,8 +105,8 @@ public class WatchService extends AbstractComponent {
WatchLockService.Lock lock = watchLockService.acquire(name);
try {
WatchStore.WatchPut result = watchStore.put(name, watchSource);
if (result.previous() == null || !result.previous().schedule().equals(result.current().schedule())) {
scheduler.add(result.current());
if (result.previous() == null || !result.previous().trigger().equals(result.current().trigger())) {
triggerService.add(result.current());
}
return result.indexResponse();
} finally {

View File

@ -15,11 +15,10 @@
"type": "string",
"index": "not_analyzed"
},
"fire_time": {
"type": "date"
},
"scheduled_fire_time": {
"type": "date"
"trigger_event" : {
"type" : "object",
"enabled" : false,
"dynamic" : true
},
"state": {
"type": "string",

View File

@ -11,7 +11,7 @@
"watch": {
"dynamic" : "strict",
"properties": {
"schedule": {
"trigger" : {
"type": "object",
"enabled" : false,
"dynamic" : true

View File

@ -6,8 +6,14 @@
package org.elasticsearch.watcher.actions.email;
import com.carrotsearch.randomizedtesting.annotations.Repeat;
import org.elasticsearch.watcher.watch.WatchExecutionContext;
import org.elasticsearch.watcher.watch.Payload;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.collect.ImmutableMap;
import org.elasticsearch.common.joda.time.DateTime;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.json.JsonXContent;
import org.elasticsearch.test.ElasticsearchTestCase;
import org.elasticsearch.watcher.actions.ActionSettingsException;
import org.elasticsearch.watcher.actions.email.service.*;
import org.elasticsearch.watcher.support.init.proxy.ScriptServiceProxy;
@ -15,23 +21,17 @@ import org.elasticsearch.watcher.support.template.ScriptTemplate;
import org.elasticsearch.watcher.support.template.Template;
import org.elasticsearch.watcher.transform.Transform;
import org.elasticsearch.watcher.transform.TransformRegistry;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.collect.ImmutableMap;
import org.elasticsearch.common.joda.time.DateTime;
import org.elasticsearch.common.joda.time.DateTimeZone;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.json.JsonXContent;
import org.elasticsearch.test.ElasticsearchTestCase;
import org.elasticsearch.watcher.watch.Payload;
import org.elasticsearch.watcher.watch.WatchExecutionContext;
import org.junit.Test;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import static org.elasticsearch.watcher.test.WatcherTestUtils.mockExecutionContext;
import static org.elasticsearch.common.joda.time.DateTimeZone.UTC;
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
import static org.elasticsearch.watcher.test.WatcherTestUtils.mockExecutionContext;
import static org.hamcrest.Matchers.*;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@ -79,10 +79,10 @@ public class EmailActionTests extends ElasticsearchTestCase {
}
};
DateTime now = DateTime.now(DateTimeZone.UTC);
DateTime now = DateTime.now(UTC);
String ctxId = randomAsciiOfLength(5);
WatchExecutionContext ctx = mockExecutionContext(now, "watch1", payload);
WatchExecutionContext ctx = mockExecutionContext("watch1", now, payload);
when(ctx.id()).thenReturn(ctxId);
if (transform != null) {
Transform.Result transformResult = mock(Transform.Result.class);
@ -92,11 +92,14 @@ public class EmailActionTests extends ElasticsearchTestCase {
}
Map<String, Object> expectedModel = ImmutableMap.<String, Object>builder()
.put("ctx", ImmutableMap.<String, Object>builder()
.put("watch_name", "watch1")
.put("payload", transform == null ? data : new Payload.Simple("_key", "_value").data())
.put("execution_time", now)
.put("fire_time", now)
.put("scheduled_fire_time", now).build())
.put("watch_name", "watch1")
.put("payload", transform == null ? data : new Payload.Simple("_key", "_value").data())
.put("execution_time", now)
.put("trigger", ImmutableMap.<String, Object>builder()
.put("triggered_time", now)
.put("scheduled_time", now)
.build())
.build())
.build();
if (subject != null) {

View File

@ -5,20 +5,22 @@
*/
package org.elasticsearch.watcher.history;
import org.elasticsearch.cluster.ClusterService;
import org.elasticsearch.common.joda.time.DateTime;
import org.elasticsearch.common.joda.time.DateTimeZone;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.test.ElasticsearchTestCase;
import org.elasticsearch.watcher.actions.Action;
import org.elasticsearch.watcher.actions.Actions;
import org.elasticsearch.watcher.condition.Condition;
import org.elasticsearch.watcher.condition.simple.AlwaysFalseCondition;
import org.elasticsearch.watcher.condition.simple.AlwaysTrueCondition;
import org.elasticsearch.watcher.input.Input;
import org.elasticsearch.watcher.scheduler.Scheduler;
import org.elasticsearch.watcher.support.clock.SystemClock;
import org.elasticsearch.watcher.throttle.Throttler;
import org.elasticsearch.watcher.transform.Transform;
import org.elasticsearch.cluster.ClusterService;
import org.elasticsearch.common.joda.time.DateTime;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.test.ElasticsearchTestCase;
import org.elasticsearch.watcher.trigger.TriggerService;
import org.elasticsearch.watcher.trigger.schedule.ScheduleTriggerEvent;
import org.elasticsearch.watcher.watch.*;
import org.junit.Before;
import org.junit.Test;
@ -52,9 +54,9 @@ public class HistoryServiceTests extends ElasticsearchTestCase {
WatchExecutor executor = mock(WatchExecutor.class);
WatchStore watchStore = mock(WatchStore.class);
WatchLockService watchLockService = mock(WatchLockService.class);
Scheduler scheduler = mock(Scheduler.class);
TriggerService triggerService = mock(TriggerService.class);
ClusterService clusterService = mock(ClusterService.class);
historyService = new HistoryService(ImmutableSettings.EMPTY, historyStore, executor, watchStore, watchLockService, scheduler, clusterService, SystemClock.INSTANCE);
historyService = new HistoryService(ImmutableSettings.EMPTY, historyStore, executor, watchStore, watchLockService, triggerService, clusterService, SystemClock.INSTANCE);
}
@Test
@ -85,7 +87,10 @@ public class HistoryServiceTests extends ElasticsearchTestCase {
when(watch.actions()).thenReturn(actions);
when(watch.status()).thenReturn(watchStatus);
WatchExecutionContext context = new WatchExecutionContext("1", watch, DateTime.now(), DateTime.now(), DateTime.now());
DateTime now = DateTime.now(DateTimeZone.UTC);
ScheduleTriggerEvent event = new ScheduleTriggerEvent(now, now);
WatchExecutionContext context = new WatchExecutionContext("1", watch, now, event);
WatchExecution watchExecution = historyService.execute(context);
assertThat(watchExecution.conditionResult(), sameInstance(conditionResult));
assertThat(watchExecution.transformResult(), sameInstance(transformResult));
@ -128,7 +133,10 @@ public class HistoryServiceTests extends ElasticsearchTestCase {
when(watch.actions()).thenReturn(actions);
when(watch.status()).thenReturn(watchStatus);
WatchExecutionContext context = new WatchExecutionContext("1", watch, DateTime.now(), DateTime.now(), DateTime.now());
DateTime now = DateTime.now(DateTimeZone.UTC);
ScheduleTriggerEvent event = new ScheduleTriggerEvent(now, now);
WatchExecutionContext context = new WatchExecutionContext("1", watch, now, event);
WatchExecution watchExecution = historyService.execute(context);
assertThat(watchExecution.inputResult(), sameInstance(inputResult));
assertThat(watchExecution.conditionResult(), sameInstance(conditionResult));
@ -171,7 +179,10 @@ public class HistoryServiceTests extends ElasticsearchTestCase {
when(watch.actions()).thenReturn(actions);
when(watch.status()).thenReturn(watchStatus);
WatchExecutionContext context = new WatchExecutionContext("1", watch, DateTime.now(), DateTime.now(), DateTime.now());
DateTime now = DateTime.now(DateTimeZone.UTC);
ScheduleTriggerEvent event = new ScheduleTriggerEvent(now, now);
WatchExecutionContext context = new WatchExecutionContext("1", watch, now, event);
WatchExecution watchExecution = historyService.execute(context);
assertThat(watchExecution.inputResult(), sameInstance(inputResult));
assertThat(watchExecution.conditionResult(), sameInstance(conditionResult));

View File

@ -13,6 +13,7 @@ import org.elasticsearch.watcher.condition.Condition;
import org.elasticsearch.watcher.condition.simple.AlwaysTrueCondition;
import org.elasticsearch.watcher.support.clock.SystemClock;
import org.elasticsearch.watcher.test.AbstractWatcherIntegrationTests;
import org.elasticsearch.watcher.trigger.schedule.ScheduleTriggerEvent;
import org.elasticsearch.watcher.watch.Watch;
import org.junit.Test;
@ -34,7 +35,8 @@ public class HistoryStoreLifeCycleTest extends AbstractWatcherIntegrationTests {
WatchRecord[] watchRecords = new WatchRecord[randomIntBetween(1, 50)];
for (int i = 0; i < watchRecords.length; i++) {
DateTime dateTime = new DateTime(i, DateTimeZone.UTC);
watchRecords[i] = new WatchRecord(watch, dateTime, dateTime);
ScheduleTriggerEvent event = new ScheduleTriggerEvent(dateTime, dateTime);
watchRecords[i] = new WatchRecord(watch, event);
historyStore.put(watchRecords[i]);
GetResponse getResponse = client().prepareGet(HistoryStore.getHistoryIndexNameForTime(dateTime), HistoryStore.DOC_TYPE, watchRecords[i].id())
.setVersion(1)
@ -54,7 +56,7 @@ public class HistoryStoreLifeCycleTest extends AbstractWatcherIntegrationTests {
assertThat(watchRecord.version(), equalTo(1l));
watchRecord.update(WatchRecord.State.EXECUTED, "_message");
historyStore.update(watchRecord);
GetResponse getResponse = client().prepareGet(HistoryStore.getHistoryIndexNameForTime(watchRecord.scheduledTime()), HistoryStore.DOC_TYPE, watchRecord.id())
GetResponse getResponse = client().prepareGet(HistoryStore.getHistoryIndexNameForTime(watchRecord.triggerEvent().triggeredTime()), HistoryStore.DOC_TYPE, watchRecord.id())
.setVersion(2l)
.get();
assertThat(getResponse.isExists(), equalTo(true));

View File

@ -34,6 +34,7 @@ import org.elasticsearch.test.ElasticsearchTestCase;
import org.elasticsearch.watcher.condition.simple.AlwaysTrueCondition;
import org.elasticsearch.watcher.support.TemplateUtils;
import org.elasticsearch.watcher.support.init.proxy.ClientProxy;
import org.elasticsearch.watcher.trigger.schedule.ScheduleTriggerEvent;
import org.elasticsearch.watcher.watch.Watch;
import org.hamcrest.core.IsNull;
import org.junit.Before;
@ -73,7 +74,8 @@ public class HistoryStoreTests extends ElasticsearchTestCase {
when(watch.condition()).thenReturn(new AlwaysTrueCondition(logger));
when(watch.input()).thenReturn(null);
when(watch.metadata()).thenReturn(null);
WatchRecord watchRecord = new WatchRecord(watch, new DateTime(0, DateTimeZone.UTC), new DateTime(0, DateTimeZone.UTC));
ScheduleTriggerEvent event = new ScheduleTriggerEvent(new DateTime(0, DateTimeZone.UTC), new DateTime(0, DateTimeZone.UTC));
WatchRecord watchRecord = new WatchRecord(watch, event);
IndexResponse indexResponse = mock(IndexResponse.class);
long version = randomLong();
@ -91,7 +93,8 @@ public class HistoryStoreTests extends ElasticsearchTestCase {
when(watch.condition()).thenReturn(new AlwaysTrueCondition(logger));
when(watch.input()).thenReturn(null);
when(watch.metadata()).thenReturn(null);
WatchRecord watchRecord = new WatchRecord(watch, new DateTime(0, DateTimeZone.UTC), new DateTime(0, DateTimeZone.UTC));
ScheduleTriggerEvent event = new ScheduleTriggerEvent(new DateTime(0, DateTimeZone.UTC), new DateTime(0, DateTimeZone.UTC));
WatchRecord watchRecord = new WatchRecord(watch, event);
watchRecord.version(4l);
IndexResponse indexResponse = mock(IndexResponse.class);

View File

@ -5,10 +5,10 @@
*/
package org.elasticsearch.watcher.history;
import org.elasticsearch.watcher.watch.Watch;
import org.elasticsearch.watcher.watch.WatchExecution;
import org.elasticsearch.watcher.watch.WatchExecutionContext;
import org.elasticsearch.watcher.watch.Payload;
import org.elasticsearch.common.joda.time.DateTime;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.watcher.actions.email.EmailAction;
import org.elasticsearch.watcher.actions.webhook.WebhookAction;
import org.elasticsearch.watcher.condition.Condition;
@ -19,12 +19,14 @@ import org.elasticsearch.watcher.input.simple.SimpleInput;
import org.elasticsearch.watcher.test.AbstractWatcherIntegrationTests;
import org.elasticsearch.watcher.test.WatcherTestUtils;
import org.elasticsearch.watcher.throttle.Throttler;
import org.elasticsearch.common.joda.time.DateTime;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.watcher.trigger.schedule.ScheduleTriggerEvent;
import org.elasticsearch.watcher.watch.Payload;
import org.elasticsearch.watcher.watch.Watch;
import org.elasticsearch.watcher.watch.WatchExecution;
import org.elasticsearch.watcher.watch.WatchExecutionContext;
import org.junit.Test;
import static org.elasticsearch.common.joda.time.DateTimeZone.UTC;
import static org.hamcrest.Matchers.equalTo;
/**
@ -34,7 +36,8 @@ public class WatchRecordTests extends AbstractWatcherIntegrationTests {
@Test
public void testParser() throws Exception {
Watch watch = WatcherTestUtils.createTestWatch("fired_test", scriptService(), httpClient(), noopEmailService(), logger);
WatchRecord watchRecord = new WatchRecord(watch, new DateTime(), new DateTime());
ScheduleTriggerEvent event = new ScheduleTriggerEvent(DateTime.now(UTC), DateTime.now(UTC));
WatchRecord watchRecord = new WatchRecord(watch, event);
XContentBuilder jsonBuilder = XContentFactory.jsonBuilder();
watchRecord.toXContent(jsonBuilder, ToXContent.EMPTY_PARAMS);
WatchRecord parsedWatchRecord = watchRecordParser().parse(jsonBuilder.bytes(), watchRecord.id(), 0);
@ -48,8 +51,9 @@ public class WatchRecordTests extends AbstractWatcherIntegrationTests {
@Test
public void testParser_WithSealedWatchRecord() throws Exception {
Watch watch = WatcherTestUtils.createTestWatch("fired_test", scriptService(), httpClient(), noopEmailService(), logger);
WatchRecord watchRecord = new WatchRecord(watch, new DateTime(), new DateTime());
WatchExecutionContext ctx = new WatchExecutionContext(watchRecord.id(), watch, new DateTime(), new DateTime(), new DateTime());
ScheduleTriggerEvent event = new ScheduleTriggerEvent(DateTime.now(UTC), DateTime.now(UTC));
WatchRecord watchRecord = new WatchRecord(watch, event);
WatchExecutionContext ctx = new WatchExecutionContext(watchRecord.id(), watch, new DateTime(), event);
ctx.onActionResult(new EmailAction.Result.Failure("failed to send because blah"));
ctx.onActionResult(new WebhookAction.Result.Executed(300, "http://localhost:8000/watchfoo", "{'awesome' : 'us'}"));
Input.Result inputResult = new SimpleInput.Result(SimpleInput.TYPE, new Payload.Simple());
@ -72,8 +76,9 @@ public class WatchRecordTests extends AbstractWatcherIntegrationTests {
@Test
public void testParser_WithSealedWatchRecord_WithScriptSearchCondition() throws Exception {
Watch watch = WatcherTestUtils.createTestWatch("fired_test", scriptService(), httpClient(), noopEmailService(), logger);
WatchRecord watchRecord = new WatchRecord(watch, new DateTime(), new DateTime());
WatchExecutionContext ctx = new WatchExecutionContext(watchRecord.id(), watch, new DateTime(), new DateTime(), new DateTime());
ScheduleTriggerEvent event = new ScheduleTriggerEvent(DateTime.now(UTC), DateTime.now(UTC));
WatchRecord watchRecord = new WatchRecord(watch, event);
WatchExecutionContext ctx = new WatchExecutionContext(watchRecord.id(), watch, new DateTime(), event);
ctx.onActionResult(new EmailAction.Result.Failure("failed to send because blah"));
ctx.onActionResult(new WebhookAction.Result.Executed(300, "http://localhost:8000/watchfoo", "{'awesome' : 'us'}"));
Input.Result inputResult = new SimpleInput.Result(SimpleInput.TYPE, new Payload.Simple());

View File

@ -25,12 +25,13 @@ import org.elasticsearch.watcher.condition.simple.AlwaysTrueCondition;
import org.elasticsearch.watcher.input.Input;
import org.elasticsearch.watcher.input.InputException;
import org.elasticsearch.watcher.input.simple.SimpleInput;
import org.elasticsearch.watcher.scheduler.schedule.IntervalSchedule;
import org.elasticsearch.watcher.support.Variables;
import org.elasticsearch.watcher.support.WatcherUtils;
import org.elasticsearch.watcher.support.clock.ClockMock;
import org.elasticsearch.watcher.support.init.proxy.ClientProxy;
import org.elasticsearch.watcher.support.init.proxy.ScriptServiceProxy;
import org.elasticsearch.watcher.trigger.schedule.IntervalSchedule;
import org.elasticsearch.watcher.trigger.schedule.ScheduleTrigger;
import org.elasticsearch.watcher.trigger.schedule.ScheduleTriggerEvent;
import org.elasticsearch.watcher.watch.Payload;
import org.elasticsearch.watcher.watch.Watch;
import org.elasticsearch.watcher.watch.WatchExecutionContext;
@ -56,7 +57,7 @@ public class SearchInputTests extends ElasticsearchIntegrationTest {
@Test
public void testExecute() throws Exception {
SearchSourceBuilder searchSourceBuilder = searchSource().query(
filteredQuery(matchQuery("event_type", "a"), rangeFilter("_timestamp").from("{{" + Variables.SCHEDULED_FIRE_TIME + "}}||-30s").to("{{" + Variables.SCHEDULED_FIRE_TIME + "}}")));
filteredQuery(matchQuery("event_type", "a"), rangeFilter("_timestamp").from("{{ctx.trigger.scheduled_time}}||-30s").to("{{ctx.trigger.triggered_time}}")));
SearchRequest request = client()
.prepareSearch()
.setSearchType(SearchInput.DEFAULT_SEARCH_TYPE)
@ -69,7 +70,7 @@ public class SearchInputTests extends ElasticsearchIntegrationTest {
WatchExecutionContext ctx = new WatchExecutionContext("test-watch",
new Watch("test-alert",
new ClockMock(),
new IntervalSchedule(new IntervalSchedule.Interval(1, IntervalSchedule.Interval.Unit.MINUTES)),
new ScheduleTrigger(new IntervalSchedule(new IntervalSchedule.Interval(1, IntervalSchedule.Interval.Unit.MINUTES))),
new SimpleInput(logger, new Payload.Simple()),
new AlwaysTrueCondition(logger),
null,
@ -77,7 +78,8 @@ public class SearchInputTests extends ElasticsearchIntegrationTest {
null,
null,
new Watch.Status()),
new DateTime(0, DateTimeZone.UTC), new DateTime(0, DateTimeZone.UTC), new DateTime(0, DateTimeZone.UTC));
new DateTime(0, DateTimeZone.UTC),
new ScheduleTriggerEvent(new DateTime(0, DateTimeZone.UTC), new DateTime(0, DateTimeZone.UTC)));
SearchInput.Result result = searchInput.execute(ctx);
assertThat((Integer) XContentMapValues.extractValue("hits.total", result.payload().data()), equalTo(0));
@ -90,7 +92,7 @@ public class SearchInputTests extends ElasticsearchIntegrationTest {
@Test
public void testDifferentSearchType() throws Exception {
SearchSourceBuilder searchSourceBuilder = searchSource().query(
filteredQuery(matchQuery("event_type", "a"), rangeFilter("_timestamp").from("{{" + Variables.SCHEDULED_FIRE_TIME + "}}||-30s").to("{{" + Variables.SCHEDULED_FIRE_TIME + "}}"))
filteredQuery(matchQuery("event_type", "a"), rangeFilter("_timestamp").from("{{ctx.trigger.scheduled_time}}||-30s").to("{{ctx.trigger.triggered_time}}"))
);
SearchType searchType = randomFrom(SearchType.values());
SearchRequest request = client()
@ -105,7 +107,7 @@ public class SearchInputTests extends ElasticsearchIntegrationTest {
WatchExecutionContext ctx = new WatchExecutionContext("test-watch",
new Watch("test-alert",
new ClockMock(),
new IntervalSchedule(new IntervalSchedule.Interval(1, IntervalSchedule.Interval.Unit.MINUTES)),
new ScheduleTrigger(new IntervalSchedule(new IntervalSchedule.Interval(1, IntervalSchedule.Interval.Unit.MINUTES))),
new SimpleInput(logger, new Payload.Simple()),
new AlwaysTrueCondition(logger),
null,
@ -113,7 +115,8 @@ public class SearchInputTests extends ElasticsearchIntegrationTest {
null,
null,
new Watch.Status()),
new DateTime(0, DateTimeZone.UTC), new DateTime(0, DateTimeZone.UTC), new DateTime(0, DateTimeZone.UTC));
new DateTime(0, DateTimeZone.UTC),
new ScheduleTriggerEvent(new DateTime(0, DateTimeZone.UTC), new DateTime(0, DateTimeZone.UTC)));
SearchInput.Result result = searchInput.execute(ctx);
assertThat((Integer) XContentMapValues.extractValue("hits.total", result.payload().data()), equalTo(0));
@ -129,7 +132,7 @@ public class SearchInputTests extends ElasticsearchIntegrationTest {
.setSearchType(SearchInput.DEFAULT_SEARCH_TYPE)
.request()
.source(searchSource()
.query(filteredQuery(matchQuery("event_type", "a"), rangeFilter("_timestamp").from("{{" + Variables.SCHEDULED_FIRE_TIME + "}}||-30s").to("{{" + Variables.SCHEDULED_FIRE_TIME + "}}"))));
.query(filteredQuery(matchQuery("event_type", "a"), rangeFilter("_timestamp").from("{{ctx.trigger.scheduled_time}}||-30s").to("{{ctx.trigger.triggered_time}}"))));
XContentBuilder builder = WatcherUtils.writeSearchRequest(request, jsonBuilder(), ToXContent.EMPTY_PARAMS);
XContentParser parser = JsonXContent.jsonXContent.createParser(builder.bytes());
@ -169,7 +172,7 @@ public class SearchInputTests extends ElasticsearchIntegrationTest {
data.put("baz", new ArrayList<String>() );
SearchSourceBuilder searchSourceBuilder = searchSource().query(
filteredQuery(matchQuery("event_type", "a"), rangeFilter("_timestamp").from("{{" + Variables.SCHEDULED_FIRE_TIME + "}}||-30s").to("{{" + Variables.SCHEDULED_FIRE_TIME + "}}")));
filteredQuery(matchQuery("event_type", "a"), rangeFilter("_timestamp").from("{{ctx.triggered.scheduled_time}}||-30s").to("{{ctx.triggered.triggered_time}}")));
SearchRequest request = client()
.prepareSearch()
.setSearchType(SearchInput.DEFAULT_SEARCH_TYPE)

View File

@ -12,7 +12,6 @@ import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.client.Client;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.metadata.IndexTemplateMetaData;
import org.elasticsearch.cluster.routing.IndexRoutingTable;
@ -45,15 +44,16 @@ import org.elasticsearch.watcher.actions.webhook.HttpClient;
import org.elasticsearch.watcher.client.WatcherClient;
import org.elasticsearch.watcher.history.HistoryStore;
import org.elasticsearch.watcher.history.WatchRecord;
import org.elasticsearch.watcher.scheduler.Scheduler;
import org.elasticsearch.watcher.scheduler.SchedulerMock;
import org.elasticsearch.watcher.scheduler.schedule.Schedule;
import org.elasticsearch.watcher.scheduler.schedule.Schedules;
import org.elasticsearch.watcher.support.WatcherUtils;
import org.elasticsearch.watcher.support.clock.ClockMock;
import org.elasticsearch.watcher.support.init.proxy.ScriptServiceProxy;
import org.elasticsearch.watcher.support.template.Template;
import org.elasticsearch.watcher.transport.actions.stats.WatcherStatsResponse;
import org.elasticsearch.watcher.trigger.ScheduleTriggerEngineMock;
import org.elasticsearch.watcher.trigger.TriggerService;
import org.elasticsearch.watcher.trigger.schedule.Schedule;
import org.elasticsearch.watcher.trigger.schedule.ScheduleTrigger;
import org.elasticsearch.watcher.trigger.schedule.Schedules;
import org.elasticsearch.watcher.watch.Watch;
import org.elasticsearch.watcher.watch.WatchService;
import org.junit.After;
@ -83,8 +83,6 @@ public abstract class AbstractWatcherIntegrationTests extends ElasticsearchInteg
private TimeWarp timeWarp;
boolean shieldEnabled = shieldEnabled();
private TransportClient shieldWatcherTransportClient;
private WatcherClient shieldWatcherClient;
@Override
protected Settings nodeSettings(int nodeOrdinal) {
@ -127,9 +125,6 @@ public abstract class AbstractWatcherIntegrationTests extends ElasticsearchInteg
// Clear all internal watcher state for the next test method:
logger.info("[{}#{}]: clearing watches", getTestClass().getSimpleName(), getTestName());
stopWatcher();
if (shieldWatcherTransportClient != null) {
shieldWatcherTransportClient.close();
}
}
@Override
@ -150,9 +145,7 @@ public abstract class AbstractWatcherIntegrationTests extends ElasticsearchInteg
private void setupTimeWarp() throws Exception {
if (timeWarped()) {
timeWarp = new TimeWarp(
internalTestCluster().getInstance(SchedulerMock.class, internalTestCluster().getMasterName()),
internalTestCluster().getInstance(ClockMock.class, internalTestCluster().getMasterName()));
timeWarp = new TimeWarp(getInstanceFromMaster(ScheduleTriggerEngineMock.class), getInstanceFromMaster(ClockMock.class));
}
}
@ -213,11 +206,12 @@ public abstract class AbstractWatcherIntegrationTests extends ElasticsearchInteg
}
protected BytesReference createWatchSource(Schedule schedule, SearchRequest conditionRequest, String conditionScript, Map<String, Object> metadata) throws IOException {
ScheduleTrigger trigger = new ScheduleTrigger(schedule);
XContentBuilder builder = jsonBuilder();
builder.startObject();
{
builder.startObject("schedule")
.field(schedule.type(), schedule)
builder.startObject("trigger")
.field(trigger.type(), trigger)
.endObject();
if (metadata != null) {
@ -266,22 +260,18 @@ public abstract class AbstractWatcherIntegrationTests extends ElasticsearchInteg
return getInstanceFromMaster(Watch.Parser.class);
}
protected Scheduler scheduler() {
return getInstanceFromMaster(Scheduler.class);
protected TriggerService triggerService() {
return getInstanceFromMaster(TriggerService.class);
}
public AbstractWatcherIntegrationTests() {
super();
}
protected WatcherClient watcherClient() {
return shieldEnabled ?
new WatcherClient(internalTestCluster().transportClient()) :
new WatcherClient(client());
// if (shieldEnabled) {
// if (shieldWatcherClient == null) {
// shieldWatcherClient = createShieldWatcherClient();
// }
// return shieldWatcherClient;
// }
// WatcherClient client = internalTestCluster().getInstance(WatcherClient.class);
// return client;
}
protected ScriptServiceProxy scriptService() {
@ -521,15 +511,15 @@ public abstract class AbstractWatcherIntegrationTests extends ElasticsearchInteg
protected static class TimeWarp {
protected final SchedulerMock scheduler;
protected final ScheduleTriggerEngineMock scheduler;
protected final ClockMock clock;
public TimeWarp(SchedulerMock scheduler, ClockMock clock) {
public TimeWarp(ScheduleTriggerEngineMock scheduler, ClockMock clock) {
this.scheduler = scheduler;
this.clock = clock;
}
public SchedulerMock scheduler() {
public ScheduleTriggerEngineMock scheduler() {
return scheduler;
}

View File

@ -5,18 +5,18 @@
*/
package org.elasticsearch.watcher.test;
import org.elasticsearch.watcher.WatcherPlugin;
import org.elasticsearch.watcher.history.WatchExecutor;
import org.elasticsearch.watcher.history.HistoryModule;
import org.elasticsearch.watcher.scheduler.SchedulerMock;
import org.elasticsearch.watcher.scheduler.SchedulerModule;
import org.elasticsearch.watcher.support.clock.Clock;
import org.elasticsearch.watcher.support.clock.ClockMock;
import org.elasticsearch.watcher.support.clock.ClockModule;
import org.elasticsearch.common.collect.ImmutableList;
import org.elasticsearch.common.inject.Module;
import org.elasticsearch.common.logging.Loggers;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.watcher.WatcherPlugin;
import org.elasticsearch.watcher.history.HistoryModule;
import org.elasticsearch.watcher.history.WatchExecutor;
import org.elasticsearch.watcher.support.clock.Clock;
import org.elasticsearch.watcher.support.clock.ClockMock;
import org.elasticsearch.watcher.support.clock.ClockModule;
import org.elasticsearch.watcher.trigger.ScheduleTriggerEngineMock;
import org.elasticsearch.watcher.trigger.TriggerModule;
import java.util.ArrayList;
import java.util.Collection;
@ -54,10 +54,10 @@ public class TimeWarpedWatcherPlugin extends WatcherPlugin {
List<Module> modules = new ArrayList<>();
for (Module module : super.spawnModules()) {
if (module instanceof SchedulerModule) {
if (module instanceof TriggerModule) {
// replacing scheduler module so we'll
// have control on when it fires a job
modules.add(new MockSchedulerModule());
modules.add(new MockTriggerModule());
} else if (module instanceof ClockModule) {
// replacing the clock module so we'll be able
@ -76,12 +76,12 @@ public class TimeWarpedWatcherPlugin extends WatcherPlugin {
return modules;
}
public static class MockSchedulerModule extends SchedulerModule {
public static class MockTriggerModule extends TriggerModule {
public MockSchedulerModule() {
super(SchedulerMock.class);
@Override
protected void registerStandardEngines() {
registerEngine(ScheduleTriggerEngineMock.class);
}
}
public static class MockClockModule extends ClockModule {

View File

@ -5,12 +5,12 @@
*/
package org.elasticsearch.watcher.test;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.collect.ImmutableMap;
import org.elasticsearch.common.joda.time.DateTime;
import org.elasticsearch.common.joda.time.DateTimeZone;
import org.elasticsearch.common.logging.ESLogger;
import org.elasticsearch.common.netty.handler.codec.http.HttpMethod;
import org.elasticsearch.common.unit.TimeValue;
@ -28,7 +28,6 @@ import org.elasticsearch.watcher.actions.webhook.HttpClient;
import org.elasticsearch.watcher.actions.webhook.WebhookAction;
import org.elasticsearch.watcher.condition.script.ScriptCondition;
import org.elasticsearch.watcher.input.search.SearchInput;
import org.elasticsearch.watcher.scheduler.schedule.CronSchedule;
import org.elasticsearch.watcher.support.Script;
import org.elasticsearch.watcher.support.WatcherUtils;
import org.elasticsearch.watcher.support.clock.SystemClock;
@ -37,11 +36,13 @@ import org.elasticsearch.watcher.support.init.proxy.ScriptServiceProxy;
import org.elasticsearch.watcher.support.template.ScriptTemplate;
import org.elasticsearch.watcher.support.template.Template;
import org.elasticsearch.watcher.transform.SearchTransform;
import org.elasticsearch.watcher.trigger.TriggerEvent;
import org.elasticsearch.watcher.trigger.schedule.CronSchedule;
import org.elasticsearch.watcher.trigger.schedule.ScheduleTrigger;
import org.elasticsearch.watcher.trigger.schedule.ScheduleTriggerEvent;
import org.elasticsearch.watcher.watch.Payload;
import org.elasticsearch.watcher.watch.Watch;
import org.elasticsearch.watcher.watch.WatchExecutionContext;
import org.hamcrest.Description;
import org.hamcrest.TypeSafeMatcher;
import javax.mail.internet.AddressException;
import java.util.ArrayList;
@ -49,6 +50,7 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import static org.elasticsearch.common.joda.time.DateTimeZone.UTC;
import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery;
import static org.elasticsearch.search.builder.SearchSourceBuilder.searchSource;
import static org.mockito.Mockito.mock;
@ -89,18 +91,17 @@ public final class WatcherTestUtils {
}
public static WatchExecutionContext mockExecutionContext(String watchName, Payload payload) {
return mockExecutionContext(DateTime.now(), watchName, payload);
return mockExecutionContext(watchName, DateTime.now(UTC), payload);
}
public static WatchExecutionContext mockExecutionContext(DateTime time, String watchName, Payload payload) {
return mockExecutionContext(time, time, time, watchName, payload);
public static WatchExecutionContext mockExecutionContext(String watchName, DateTime time, Payload payload) {
return mockExecutionContext(watchName, time, new ScheduleTriggerEvent(time, time), payload);
}
public static WatchExecutionContext mockExecutionContext(DateTime executionTime, DateTime firedTime, DateTime scheduledTime, String watchName, Payload payload) {
public static WatchExecutionContext mockExecutionContext(String watchName, DateTime executionTime, TriggerEvent event, Payload payload) {
WatchExecutionContext ctx = mock(WatchExecutionContext.class);
when(ctx.executionTime()).thenReturn(executionTime);
when(ctx.fireTime()).thenReturn(firedTime);
when(ctx.scheduledTime()).thenReturn(scheduledTime);
when(ctx.triggerEvent()).thenReturn(event);
Watch watch = mock(Watch.class);
when(watch.name()).thenReturn(watchName);
when(ctx.watch()).thenReturn(watch);
@ -143,7 +144,7 @@ public final class WatcherTestUtils {
return new Watch(
watchName,
SystemClock.INSTANCE,
new CronSchedule("0/5 * * * * ? *"),
new ScheduleTrigger(new CronSchedule("0/5 * * * * ? *")),
new SearchInput(logger, scriptService, ClientProxy.of(ElasticsearchIntegrationTest.client()), conditionRequest),
new ScriptCondition(logger, scriptService, new Script("return true")),
new SearchTransform(logger, scriptService, ClientProxy.of(ElasticsearchIntegrationTest.client()), transformRequest),

View File

@ -17,10 +17,9 @@ import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.watcher.WatcherPlugin;
import org.elasticsearch.watcher.client.WatchSourceBuilder;
import org.elasticsearch.watcher.client.WatcherClient;
import org.elasticsearch.watcher.scheduler.Scheduler;
import org.elasticsearch.watcher.scheduler.SchedulerMock;
import org.elasticsearch.watcher.scheduler.SchedulerModule;
import org.elasticsearch.watcher.transport.actions.put.PutWatchRequest;
import org.elasticsearch.watcher.trigger.ScheduleTriggerEngineMock;
import org.elasticsearch.watcher.trigger.TriggerModule;
import java.util.ArrayList;
import java.util.Collection;
@ -29,7 +28,8 @@ import java.util.List;
import static org.elasticsearch.watcher.actions.ActionBuilders.indexAction;
import static org.elasticsearch.watcher.condition.ConditionBuilders.scriptCondition;
import static org.elasticsearch.watcher.input.InputBuilders.searchInput;
import static org.elasticsearch.watcher.scheduler.schedule.Schedules.interval;
import static org.elasticsearch.watcher.trigger.TriggerBuilders.schedule;
import static org.elasticsearch.watcher.trigger.schedule.Schedules.interval;
/**
*/
@ -51,7 +51,7 @@ public class WatcherBenchmark {
for (int i = 0; i < numAlerts; i++) {
final String name = "_name" + i;
PutWatchRequest putAlertRequest = new PutWatchRequest(name, new WatchSourceBuilder()
.schedule(interval("5s"))
.trigger(schedule(interval("5s")))
.input(searchInput(new SearchRequest().source(
new SearchSourceBuilder()
)))
@ -63,7 +63,7 @@ public class WatcherBenchmark {
int numThreads = 50;
int watchersPerThread = numAlerts / numThreads;
final SchedulerMock scheduler = (SchedulerMock) node.injector().getInstance(Scheduler.class);
final ScheduleTriggerEngineMock scheduler = node.injector().getInstance(ScheduleTriggerEngineMock.class);
Thread[] threads = new Thread[numThreads];
for (int i = 0; i < numThreads; i++) {
final int begin = i * watchersPerThread;
@ -73,7 +73,7 @@ public class WatcherBenchmark {
public void run() {
while (true) {
for (int j = begin; j < end; j++) {
scheduler.fire("_name" + j);
scheduler.trigger("_name" + j);
}
}
}
@ -110,10 +110,10 @@ public class WatcherBenchmark {
public Iterable<? extends Module> spawnModules() {
List<Module> modules = new ArrayList<>();
for (Module module : super.spawnModules()) {
if (module instanceof SchedulerModule) {
if (module instanceof TriggerModule) {
// replacing scheduler module so we'll
// have control on when it fires a job
modules.add(new MockSchedulerModule());
modules.add(new MockTriggerModule());
} else {
modules.add(module);
@ -122,10 +122,11 @@ public class WatcherBenchmark {
return modules;
}
public static class MockSchedulerModule extends SchedulerModule {
public static class MockTriggerModule extends TriggerModule {
public MockSchedulerModule() {
super(SchedulerMock.class);
@Override
protected void registerStandardEngines() {
registerEngine(ScheduleTriggerEngineMock.class);
}
}

View File

@ -13,12 +13,13 @@ import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.watcher.WatcherException;
import org.elasticsearch.watcher.client.WatchSourceBuilder;
import org.elasticsearch.watcher.client.WatcherClient;
import org.elasticsearch.watcher.scheduler.schedule.IntervalSchedule;
import org.elasticsearch.watcher.support.WatcherUtils;
import org.elasticsearch.watcher.test.AbstractWatcherIntegrationTests;
import org.elasticsearch.watcher.transport.actions.delete.DeleteWatchResponse;
import org.elasticsearch.watcher.transport.actions.get.GetWatchResponse;
import org.elasticsearch.watcher.transport.actions.put.PutWatchResponse;
import org.elasticsearch.watcher.trigger.schedule.IntervalSchedule;
import org.elasticsearch.watcher.trigger.schedule.Schedules;
import org.elasticsearch.watcher.watch.WatchStore;
import org.junit.Test;
@ -32,10 +33,9 @@ import static org.elasticsearch.watcher.actions.ActionBuilders.indexAction;
import static org.elasticsearch.watcher.client.WatchSourceBuilder.watchSourceBuilder;
import static org.elasticsearch.watcher.condition.ConditionBuilders.scriptCondition;
import static org.elasticsearch.watcher.input.InputBuilders.searchInput;
import static org.elasticsearch.watcher.scheduler.schedule.Schedules.cron;
import static org.elasticsearch.watcher.scheduler.schedule.Schedules.interval;
import static org.elasticsearch.watcher.support.Variables.*;
import static org.elasticsearch.watcher.test.WatcherTestUtils.newInputSearchRequest;
import static org.elasticsearch.watcher.trigger.TriggerBuilders.schedule;
import static org.elasticsearch.watcher.trigger.schedule.Schedules.interval;
import static org.hamcrest.Matchers.*;
/**
@ -52,13 +52,13 @@ public class BasicWatcherTests extends AbstractWatcherIntegrationTests {
SearchRequest searchRequest = newInputSearchRequest("idx").source(searchSource().query(termQuery("field", "value")));
watcherClient.preparePutWatch("_name")
.source(watchSourceBuilder()
.schedule(interval(5, IntervalSchedule.Interval.Unit.SECONDS))
.trigger(schedule(interval(5, IntervalSchedule.Interval.Unit.SECONDS)))
.input(searchInput(searchRequest))
.condition(scriptCondition("ctx.payload.hits.total == 1")))
.get();
if (timeWarped()) {
timeWarp().scheduler().fire("_name");
timeWarp().scheduler().trigger("_name");
refresh();
}
@ -75,13 +75,13 @@ public class BasicWatcherTests extends AbstractWatcherIntegrationTests {
SearchRequest searchRequest = newInputSearchRequest("idx").source(searchSource().query(termQuery("field", "value")));
watcherClient.preparePutWatch("_name")
.source(watchSourceBuilder()
.schedule(interval(5, IntervalSchedule.Interval.Unit.SECONDS))
.trigger(schedule(interval(5, IntervalSchedule.Interval.Unit.SECONDS)))
.input(searchInput(searchRequest))
.condition(scriptCondition("ctx.payload.hits.total == 1")))
.get();
if (timeWarped()) {
timeWarp().scheduler().fire("_name");
timeWarp().scheduler().trigger("_name");
refresh();
}
@ -93,7 +93,7 @@ public class BasicWatcherTests extends AbstractWatcherIntegrationTests {
refresh();
if (timeWarped()) {
timeWarp().scheduler().fire("_name");
timeWarp().scheduler().trigger("_name");
refresh();
}
@ -106,7 +106,7 @@ public class BasicWatcherTests extends AbstractWatcherIntegrationTests {
SearchRequest searchRequest = newInputSearchRequest("idx").source(searchSource().query(matchAllQuery()));
PutWatchResponse indexResponse = watcherClient.preparePutWatch("_name")
.source(watchSourceBuilder()
.schedule(interval(5, IntervalSchedule.Interval.Unit.SECONDS))
.trigger(schedule(interval(5, IntervalSchedule.Interval.Unit.SECONDS)))
.input(searchInput(searchRequest))
.condition(scriptCondition("ctx.payload.hits.total == 1")))
.get();
@ -174,7 +174,7 @@ public class BasicWatcherTests extends AbstractWatcherIntegrationTests {
.source(searchSource().query(matchAllQuery()));
WatchSourceBuilder source = watchSourceBuilder()
.schedule(interval("5s"))
.trigger(schedule(interval("5s")))
.input(searchInput(searchRequest))
.addAction(indexAction("idx", "action"));
@ -183,7 +183,7 @@ public class BasicWatcherTests extends AbstractWatcherIntegrationTests {
.get();
if (timeWarped()) {
timeWarp().scheduler().fire("_name");
timeWarp().scheduler().trigger("_name");
refresh();
}
assertWatchWithMinimumPerformedActionsCount("_name", 0, false);
@ -193,17 +193,19 @@ public class BasicWatcherTests extends AbstractWatcherIntegrationTests {
.get();
if (timeWarped()) {
timeWarp().scheduler().fire("_name");
timeWarp().scheduler().trigger("_name");
refresh();
}
assertWatchWithMinimumPerformedActionsCount("_name", 1, false);
watcherClient().preparePutWatch("_name")
.source(source.schedule(cron("0/1 * * * * ? 2020")).condition(scriptCondition("ctx.payload.hits.total == 0")))
.source(source
.trigger(schedule(Schedules.cron("0/1 * * * * ? 2020")))
.condition(scriptCondition("ctx.payload.hits.total == 0")))
.get();
if (timeWarped()) {
timeWarp().scheduler().fire("_name");
timeWarp().scheduler().trigger("_name");
refresh();
} else {
Thread.sleep(1000);
@ -212,7 +214,7 @@ public class BasicWatcherTests extends AbstractWatcherIntegrationTests {
long count = findNumberOfPerformedActions("_name");
if (timeWarped()) {
timeWarp().scheduler().fire("_name");
timeWarp().scheduler().trigger("_name");
refresh();
} else {
Thread.sleep(1000);
@ -223,7 +225,7 @@ public class BasicWatcherTests extends AbstractWatcherIntegrationTests {
@Test
public void testConditionSearchWithSource() throws Exception {
String variable = randomFrom(EXECUTION_TIME, SCHEDULED_FIRE_TIME, FIRE_TIME);
String variable = randomFrom("ctx.execution_time", "ctx.trigger.scheduled_time", "ctx.trigger.triggered_time");
SearchSourceBuilder searchSourceBuilder = searchSource().query(filteredQuery(
matchQuery("level", "a"),
rangeFilter("_timestamp")
@ -235,7 +237,7 @@ public class BasicWatcherTests extends AbstractWatcherIntegrationTests {
@Test
public void testConditionSearchWithIndexedTemplate() throws Exception {
String variable = randomFrom(EXECUTION_TIME, SCHEDULED_FIRE_TIME, FIRE_TIME);
String variable = randomFrom("ctx.execution_time", "ctx.trigger.scheduled_time", "ctx.trigger.triggered_time");
SearchSourceBuilder searchSourceBuilder = searchSource().query(filteredQuery(
matchQuery("level", "a"),
rangeFilter("_timestamp")
@ -274,7 +276,7 @@ public class BasicWatcherTests extends AbstractWatcherIntegrationTests {
refresh();
if (timeWarped()) {
timeWarp().clock().fastForwardSeconds(5);
timeWarp().scheduler().fire(watchName);
timeWarp().scheduler().trigger(watchName);
refresh();
} else {
Thread.sleep(5000);
@ -288,7 +290,7 @@ public class BasicWatcherTests extends AbstractWatcherIntegrationTests {
refresh();
if (timeWarped()) {
timeWarp().clock().fastForwardSeconds(5);
timeWarp().scheduler().fire(watchName);
timeWarp().scheduler().trigger(watchName);
refresh();
} else {
Thread.sleep(5000);
@ -302,7 +304,7 @@ public class BasicWatcherTests extends AbstractWatcherIntegrationTests {
refresh();
if (timeWarped()) {
timeWarp().clock().fastForwardSeconds(5);
timeWarp().scheduler().fire(watchName);
timeWarp().scheduler().trigger(watchName);
refresh();
} else {
Thread.sleep(5000);

View File

@ -8,16 +8,19 @@ package org.elasticsearch.watcher.test.integration;
import org.elasticsearch.action.WriteConsistencyLevel;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.watcher.watch.Watch;
import org.elasticsearch.watcher.watch.WatchService;
import org.elasticsearch.watcher.watch.WatchStore;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.joda.time.DateTime;
import org.elasticsearch.common.joda.time.DateTimeZone;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.test.junit.annotations.TestLogging;
import org.elasticsearch.watcher.actions.Action;
import org.elasticsearch.watcher.actions.Actions;
import org.elasticsearch.watcher.condition.script.ScriptCondition;
import org.elasticsearch.watcher.history.WatchRecord;
import org.elasticsearch.watcher.history.HistoryStore;
import org.elasticsearch.watcher.history.WatchRecord;
import org.elasticsearch.watcher.input.search.SearchInput;
import org.elasticsearch.watcher.scheduler.schedule.CronSchedule;
import org.elasticsearch.watcher.support.Script;
import org.elasticsearch.watcher.support.clock.SystemClock;
import org.elasticsearch.watcher.support.init.proxy.ClientProxy;
@ -26,13 +29,12 @@ import org.elasticsearch.watcher.test.WatcherTestUtils;
import org.elasticsearch.watcher.transform.SearchTransform;
import org.elasticsearch.watcher.transport.actions.put.PutWatchResponse;
import org.elasticsearch.watcher.transport.actions.stats.WatcherStatsResponse;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.joda.time.DateTime;
import org.elasticsearch.common.joda.time.DateTimeZone;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.test.junit.annotations.TestLogging;
import org.elasticsearch.watcher.trigger.schedule.CronSchedule;
import org.elasticsearch.watcher.trigger.schedule.ScheduleTrigger;
import org.elasticsearch.watcher.trigger.schedule.ScheduleTriggerEvent;
import org.elasticsearch.watcher.watch.Watch;
import org.elasticsearch.watcher.watch.WatchService;
import org.elasticsearch.watcher.watch.WatchStore;
import org.junit.Test;
import java.util.ArrayList;
@ -82,7 +84,7 @@ public class BootStrapTests extends AbstractWatcherIntegrationTests {
Watch watch = new Watch(
"test-serialization",
SystemClock.INSTANCE,
new CronSchedule("0/5 * * * * ? 2035"), //Set this into the future so we don't get any extra runs
new ScheduleTrigger(new CronSchedule("0/5 * * * * ? 2035")), //Set this into the future so we don't get any extra runs
new SearchInput(logger, scriptService(), ClientProxy.of(client()), searchRequest),
new ScriptCondition(logger, scriptService(), new Script("return true")),
new SearchTransform(logger, scriptService(), ClientProxy.of(client()), searchRequest),
@ -98,9 +100,10 @@ public class BootStrapTests extends AbstractWatcherIntegrationTests {
refresh();
assertThat(indexResponse.isCreated(), is(true));
DateTime scheduledFireTime = new DateTime(DateTimeZone.UTC);
WatchRecord watchRecord = new WatchRecord(watch, scheduledFireTime, scheduledFireTime);
String actionHistoryIndex = HistoryStore.getHistoryIndexNameForTime(scheduledFireTime);
DateTime now = DateTime.now(DateTimeZone.UTC);
ScheduleTriggerEvent event = new ScheduleTriggerEvent(now, now);
WatchRecord watchRecord = new WatchRecord(watch, event);
String actionHistoryIndex = HistoryStore.getHistoryIndexNameForTime(now);
createIndex(actionHistoryIndex);
ensureGreen(actionHistoryIndex);
@ -141,7 +144,7 @@ public class BootStrapTests extends AbstractWatcherIntegrationTests {
Watch watch = new Watch(
"action-test-" + i + " " + j,
SystemClock.INSTANCE,
new CronSchedule("0/5 * * * * ? 2035"), //Set a cron schedule far into the future so this watch is never scheduled
new ScheduleTrigger(new CronSchedule("0/5 * * * * ? 2035")), //Set a cron schedule far into the future so this watch is never scheduled
new SearchInput(logger, scriptService(), ClientProxy.of(client()),
searchRequest),
new ScriptCondition(logger, scriptService(), new Script("return true")),
@ -156,7 +159,8 @@ public class BootStrapTests extends AbstractWatcherIntegrationTests {
PutWatchResponse putWatchResponse = watcherClient().preparePutWatch(watch.name()).source(jsonBuilder.bytes()).get();
assertThat(putWatchResponse.indexResponse().isCreated(), is(true));
WatchRecord watchRecord = new WatchRecord(watch, historyIndexDate, historyIndexDate);
ScheduleTriggerEvent event = new ScheduleTriggerEvent(historyIndexDate, historyIndexDate);
WatchRecord watchRecord = new WatchRecord(watch, event);
XContentBuilder jsonBuilder2 = jsonBuilder();
watchRecord.toXContent(jsonBuilder2, ToXContent.EMPTY_PARAMS);

View File

@ -7,13 +7,13 @@ package org.elasticsearch.watcher.test.integration;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.watcher.scheduler.schedule.IntervalSchedule.Interval;
import org.elasticsearch.watcher.test.AbstractWatcherIntegrationTests;
import org.elasticsearch.watcher.test.WatcherTestUtils;
import org.elasticsearch.watcher.transform.SearchTransform;
import org.elasticsearch.watcher.transport.actions.put.PutWatchResponse;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.watcher.trigger.schedule.IntervalSchedule;
import org.junit.Test;
import java.util.HashMap;
@ -22,10 +22,11 @@ import java.util.Map;
import static org.elasticsearch.watcher.actions.ActionBuilders.indexAction;
import static org.elasticsearch.watcher.client.WatchSourceBuilder.watchSourceBuilder;
import static org.elasticsearch.watcher.input.InputBuilders.searchInput;
import static org.elasticsearch.watcher.scheduler.schedule.Schedules.interval;
import static org.elasticsearch.watcher.transform.TransformBuilders.searchTransform;
import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery;
import static org.elasticsearch.search.builder.SearchSourceBuilder.searchSource;
import static org.elasticsearch.watcher.trigger.TriggerBuilders.schedule;
import static org.elasticsearch.watcher.trigger.schedule.Schedules.interval;
import static org.hamcrest.Matchers.*;
/**
@ -50,17 +51,17 @@ public class TransformSearchTests extends AbstractWatcherIntegrationTests {
PutWatchResponse putWatchResponse = watcherClient().preparePutWatch("test-payload")
.source(watchSourceBuilder()
.schedule(interval(5, Interval.Unit.SECONDS))
.trigger(schedule(interval(5, IntervalSchedule.Interval.Unit.SECONDS)))
.input(searchInput(inputRequest))
.transform(searchTransform(transformRequest))
.addAction(indexAction("my-payload-output", "result"))
.metadata(metadata)
.throttlePeriod(TimeValue.timeValueSeconds(0)))
.get();
.get();
assertThat(putWatchResponse.indexResponse().isCreated(), is(true));
if (timeWarped()) {
timeWarp().scheduler().fire("test-payload");
timeWarp().scheduler().trigger("test-payload");
refresh();
}

View File

@ -19,10 +19,11 @@ import java.util.Map;
import static org.elasticsearch.watcher.client.WatchSourceBuilder.watchSourceBuilder;
import static org.elasticsearch.watcher.condition.ConditionBuilders.scriptCondition;
import static org.elasticsearch.watcher.input.InputBuilders.searchInput;
import static org.elasticsearch.watcher.scheduler.schedule.Schedules.cron;
import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery;
import static org.elasticsearch.index.query.QueryBuilders.termQuery;
import static org.elasticsearch.search.builder.SearchSourceBuilder.searchSource;
import static org.elasticsearch.watcher.trigger.TriggerBuilders.schedule;
import static org.elasticsearch.watcher.trigger.schedule.Schedules.cron;
import static org.hamcrest.Matchers.greaterThan;
/**
@ -43,14 +44,14 @@ public class WatchMetadataTests extends AbstractWatcherIntegrationTests {
metadata.put("baz", metaList);
watcherClient().preparePutWatch("_name")
.source(watchSourceBuilder()
.schedule(cron("0/5 * * * * ? *"))
.trigger(schedule(cron("0/5 * * * * ? *")))
.input(searchInput(WatcherTestUtils.newInputSearchRequest("my-index").source(searchSource().query(matchAllQuery()))))
.condition(scriptCondition("ctx.payload.hits.total == 1"))
.metadata(metadata))
.get();
.get();
if (timeWarped()) {
timeWarp().scheduler().fire("_name");
timeWarp().scheduler().trigger("_name");
} else {
// Wait for a no action entry to be added. (the condition search request will not match, because there are no docs in my-index)
assertWatchWithNoActionNeeded("_name", 1);

View File

@ -63,7 +63,7 @@ public class WatchStatsTests extends AbstractWatcherIntegrationTests {
.get();
if (timeWarped()) {
timeWarp().scheduler().fire("_name", 30, TimeValue.timeValueSeconds(1));
timeWarp().scheduler().trigger("_name", 30, TimeValue.timeValueSeconds(1));
} else {
//Wait a little until we should have queued an action
Thread.sleep(TimeUnit.SECONDS.toMillis(5));

View File

@ -7,30 +7,31 @@ package org.elasticsearch.watcher.test.integration;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.watcher.watch.Watch;
import org.elasticsearch.common.joda.time.DateTime;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.watcher.actions.ActionBuilders;
import org.elasticsearch.watcher.client.WatcherClient;
import org.elasticsearch.watcher.history.WatchRecord;
import org.elasticsearch.watcher.history.HistoryStore;
import org.elasticsearch.watcher.history.WatchRecord;
import org.elasticsearch.watcher.test.AbstractWatcherIntegrationTests;
import org.elasticsearch.watcher.transport.actions.ack.AckWatchResponse;
import org.elasticsearch.watcher.transport.actions.get.GetWatchResponse;
import org.elasticsearch.watcher.transport.actions.put.PutWatchResponse;
import org.elasticsearch.common.joda.time.DateTime;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.watcher.watch.Watch;
import org.junit.Test;
import java.util.concurrent.TimeUnit;
import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery;
import static org.elasticsearch.index.query.QueryBuilders.matchQuery;
import static org.elasticsearch.watcher.client.WatchSourceBuilder.watchSourceBuilder;
import static org.elasticsearch.watcher.condition.ConditionBuilders.scriptCondition;
import static org.elasticsearch.watcher.input.InputBuilders.searchInput;
import static org.elasticsearch.watcher.scheduler.schedule.Schedules.cron;
import static org.elasticsearch.watcher.scheduler.schedule.Schedules.interval;
import static org.elasticsearch.watcher.test.WatcherTestUtils.matchAllRequest;
import static org.elasticsearch.watcher.transform.TransformBuilders.searchTransform;
import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery;
import static org.elasticsearch.index.query.QueryBuilders.matchQuery;
import static org.elasticsearch.watcher.trigger.TriggerBuilders.schedule;
import static org.elasticsearch.watcher.trigger.schedule.Schedules.cron;
import static org.elasticsearch.watcher.trigger.schedule.Schedules.interval;
import static org.hamcrest.Matchers.*;
import static org.hamcrest.core.IsEqual.equalTo;
@ -53,7 +54,7 @@ public class WatchThrottleTests extends AbstractWatcherIntegrationTests {
PutWatchResponse putWatchResponse = watcherClient.preparePutWatch()
.watchName("_name")
.source(watchSourceBuilder()
.schedule(cron("0/5 * * * * ? *"))
.trigger(schedule(cron("0/5 * * * * ? *")))
.input(searchInput(matchAllRequest().indices("events")))
.condition(scriptCondition("ctx.payload.hits.total > 0"))
.transform(searchTransform(matchAllRequest().indices("events")))
@ -63,7 +64,7 @@ public class WatchThrottleTests extends AbstractWatcherIntegrationTests {
assertThat(putWatchResponse.indexResponse().isCreated(), is(true));
if (timeWarped()) {
timeWarp().scheduler().fire("_name", 4, TimeValue.timeValueSeconds(5));
timeWarp().scheduler().trigger("_name", 4, TimeValue.timeValueSeconds(5));
} else {
Thread.sleep(20000);
}
@ -75,7 +76,7 @@ public class WatchThrottleTests extends AbstractWatcherIntegrationTests {
assertThat(countAfterAck, greaterThanOrEqualTo((long) 1));
if (timeWarped()) {
timeWarp().scheduler().fire("_name", 4, TimeValue.timeValueSeconds(5));
timeWarp().scheduler().trigger("_name", 4, TimeValue.timeValueSeconds(5));
} else {
Thread.sleep(20000);
}
@ -91,7 +92,7 @@ public class WatchThrottleTests extends AbstractWatcherIntegrationTests {
refresh();
if (timeWarped()) {
timeWarp().scheduler().fire("_name", 4, TimeValue.timeValueSeconds(5));
timeWarp().scheduler().trigger("_name", 4, TimeValue.timeValueSeconds(5));
} else {
Thread.sleep(20000);
}
@ -124,7 +125,7 @@ public class WatchThrottleTests extends AbstractWatcherIntegrationTests {
PutWatchResponse putWatchResponse = watcherClient.preparePutWatch()
.watchName("_name")
.source(watchSourceBuilder()
.schedule(interval("5s"))
.trigger(schedule(interval("5s")))
.input(searchInput(matchAllRequest().indices("events")))
.condition(scriptCondition("ctx.payload.hits.total > 0"))
.transform(searchTransform(matchAllRequest().indices("events")))
@ -136,7 +137,7 @@ public class WatchThrottleTests extends AbstractWatcherIntegrationTests {
if (timeWarped()) {
timeWarp().clock().setTime(DateTime.now());
timeWarp().scheduler().fire("_name");
timeWarp().scheduler().trigger("_name");
refresh();
// the first fire should work
@ -144,7 +145,7 @@ public class WatchThrottleTests extends AbstractWatcherIntegrationTests {
assertThat(actionsCount, is(1L));
timeWarp().clock().fastForwardSeconds(5);
timeWarp().scheduler().fire("_name");
timeWarp().scheduler().trigger("_name");
refresh();
// the last fire should have been throttled, so number of actions shouldn't change
@ -152,7 +153,7 @@ public class WatchThrottleTests extends AbstractWatcherIntegrationTests {
assertThat(actionsCount, is(1L));
timeWarp().clock().fastForwardSeconds(10);
timeWarp().scheduler().fire("_name");
timeWarp().scheduler().trigger("_name");
refresh();
// the last fire occurred passed the throttle period, so a new action should have been added

View File

@ -5,14 +5,14 @@
*/
package org.elasticsearch.watcher.transform;
import org.elasticsearch.watcher.watch.WatchExecutionContext;
import org.elasticsearch.watcher.watch.Payload;
import org.elasticsearch.common.collect.ImmutableList;
import org.elasticsearch.common.collect.ImmutableMap;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.json.JsonXContent;
import org.elasticsearch.test.ElasticsearchTestCase;
import org.elasticsearch.watcher.watch.Payload;
import org.elasticsearch.watcher.watch.WatchExecutionContext;
import org.junit.Test;
import java.io.IOException;

View File

@ -15,9 +15,9 @@ import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.json.JsonXContent;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.watcher.support.Variables;
import org.elasticsearch.watcher.support.init.proxy.ClientProxy;
import org.elasticsearch.watcher.test.AbstractWatcherIntegrationTests;
import org.elasticsearch.watcher.trigger.schedule.ScheduleTriggerEvent;
import org.elasticsearch.watcher.watch.Payload;
import org.elasticsearch.watcher.watch.WatchExecutionContext;
import org.junit.Test;
@ -104,13 +104,14 @@ public class SearchTransformTests extends AbstractWatcherIntegrationTests {
refresh();
SearchRequest request = Requests.searchRequest("idx").source(searchSource().query(filteredQuery(matchAllQuery(), boolFilter()
.must(rangeFilter("date").gt("{{" + Variables.CTX + "." + Variables.SCHEDULED_FIRE_TIME + "}}"))
.must(rangeFilter("date").lt("{{" + Variables.CTX + "." + Variables.EXECUTION_TIME + "}}"))
.must(termFilter("value", "{{" + Variables.CTX + "." + Variables.PAYLOAD + ".value}}")))));
.must(rangeFilter("date").gt("{{ctx.trigger.scheduled_time}}"))
.must(rangeFilter("date").lt("{{ctx.execution_time}}"))
.must(termFilter("value", "{{ctx.payload.value}}")))));
SearchTransform transform = new SearchTransform(logger, scriptService(), ClientProxy.of(client()), request);
WatchExecutionContext ctx = mockExecutionContext(parseDate("2015-01-04T00:00:00"), parseDate("2015-01-04T00:00:00"), parseDate("2015-01-01T00:00:00"), "_name", EMPTY_PAYLOAD);
ScheduleTriggerEvent event = new ScheduleTriggerEvent(parseDate("2015-01-04T00:00:00"), parseDate("2015-01-01T00:00:00"));
WatchExecutionContext ctx = mockExecutionContext("_name", parseDate("2015-01-04T00:00:00"), event, EMPTY_PAYLOAD);
Payload payload = simplePayload("value", "val_3");
@ -140,7 +141,6 @@ public class SearchTransformTests extends AbstractWatcherIntegrationTests {
SearchType searchType = randomBoolean() ? null : randomFrom(SearchType.values());
String templateName = randomBoolean() ? null : "template1";
ScriptService.ScriptType templateType = templateName != null && randomBoolean() ? randomFrom(ScriptService.ScriptType.values()) : null;
BytesReference source = null;
XContentBuilder builder = jsonBuilder().startObject();
if (indices != null) {
builder.array("indices", indices);
@ -163,7 +163,7 @@ public class SearchTransformTests extends AbstractWatcherIntegrationTests {
.endObject()
.endObject()
.endObject();
source = sourceBuilder.bytes();
BytesReference source = sourceBuilder.bytes();
builder.startObject("body")
.startObject("query")

View File

@ -3,42 +3,58 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.watcher.scheduler;
package org.elasticsearch.watcher.trigger;
import org.elasticsearch.watcher.support.clock.Clock;
import org.elasticsearch.watcher.support.clock.ClockMock;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.joda.time.DateTime;
import org.elasticsearch.common.logging.ESLogger;
import org.elasticsearch.common.logging.Loggers;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.watcher.support.clock.Clock;
import org.elasticsearch.watcher.support.clock.ClockMock;
import org.elasticsearch.watcher.trigger.schedule.ScheduleTriggerEngine;
import org.elasticsearch.watcher.trigger.schedule.ScheduleRegistry;
import org.elasticsearch.watcher.trigger.schedule.ScheduleTrigger;
import org.elasticsearch.watcher.trigger.schedule.ScheduleTriggerEvent;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* A mock scheduler to help with unit testing. Provide {@link SchedulerMock#fire} method to manually trigger
* A mock scheduler to help with unit testing. Provide {@link ScheduleTriggerEngineMock#trigger} method to manually trigger
* jobs.
*/
public class SchedulerMock implements Scheduler {
public class ScheduleTriggerEngineMock extends ScheduleTriggerEngine {
private final ESLogger logger;
private final List<Listener> listeners = new CopyOnWriteArrayList<>();
private final ConcurrentMap<String, Job> jobs = new ConcurrentHashMap<>();
private final Clock clock;
private final ScheduleRegistry scheduleRegistry;
@Inject
public SchedulerMock(Settings settings, Clock clock) {
this.logger = Loggers.getLogger(SchedulerMock.class, settings);
public ScheduleTriggerEngineMock(Settings settings, ScheduleRegistry scheduleRegistry, Clock clock) {
super(settings);
this.logger = Loggers.getLogger(ScheduleTriggerEngineMock.class, settings);
this.scheduleRegistry = scheduleRegistry;
this.clock = clock;
}
@Override
public void start(Collection<? extends Job> jobs) {
public ScheduleTrigger parseTrigger(String context, XContentParser parser) throws IOException {
return new ScheduleTrigger(scheduleRegistry.parse(context, parser));
}
@Override
public ScheduleTriggerEvent parseTriggerEvent(String context, XContentParser parser) throws IOException {
return ScheduleTriggerEvent.parse(context, parser);
}
@Override
public void start(Collection<Job> jobs) {
}
@Override
@ -55,25 +71,21 @@ public class SchedulerMock implements Scheduler {
return jobs.remove(jobName) != null;
}
@Override
public void addListener(Listener listener) {
listeners.add(listener);
public void trigger(String jobName) {
trigger(jobName, 1, null);
}
public void fire(String jobName) {
fire(jobName, 1, null);
public void trigger(String jobName, int times) {
trigger(jobName, times, null);
}
public void fire(String jobName, int times) {
fire(jobName, times, null);
}
public void fire(String jobName, int times, TimeValue interval) {
public void trigger(String jobName, int times, TimeValue interval) {
for (int i = 0; i < times; i++) {
DateTime now = clock.now();
logger.debug("firing [" + jobName + "] at [" + now + "]");
ScheduleTriggerEvent event = new ScheduleTriggerEvent(now, now);
for (Listener listener : listeners) {
listener.fire(jobName, now, now);
listener.triggered(jobName, event);
}
if (clock instanceof ClockMock) {
((ClockMock) clock).fastForward(interval == null ? TimeValue.timeValueMillis(10) : interval);

View File

@ -3,7 +3,7 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.watcher.scheduler.schedule;
package org.elasticsearch.watcher.trigger.schedule;
import org.elasticsearch.watcher.WatcherSettingsException;
import org.elasticsearch.common.bytes.BytesReference;

View File

@ -3,16 +3,16 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.watcher.scheduler.schedule;
package org.elasticsearch.watcher.trigger.schedule;
import com.carrotsearch.randomizedtesting.annotations.Repeat;
import org.elasticsearch.watcher.WatcherSettingsException;
import org.elasticsearch.watcher.scheduler.schedule.support.DayTimes;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.primitives.Ints;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.json.JsonXContent;
import org.elasticsearch.watcher.trigger.schedule.support.DayTimes;
import org.junit.Test;
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;

View File

@ -3,7 +3,7 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.watcher.scheduler.schedule;
package org.elasticsearch.watcher.trigger.schedule;
import com.carrotsearch.randomizedtesting.annotations.Repeat;
import org.elasticsearch.watcher.WatcherSettingsException;

View File

@ -3,10 +3,9 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.watcher.scheduler.schedule;
package org.elasticsearch.watcher.trigger.schedule;
import org.elasticsearch.watcher.WatcherSettingsException;
import org.elasticsearch.watcher.scheduler.schedule.IntervalSchedule.Interval.Unit;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
@ -66,7 +65,7 @@ public class IntervalScheduleTests extends ElasticsearchTestCase {
}
private static IntervalSchedule.Interval randomTimeValue() {
Unit unit = Unit.values()[randomIntBetween(0, Unit.values().length - 1)];
IntervalSchedule.Interval.Unit unit = IntervalSchedule.Interval.Unit.values()[randomIntBetween(0, IntervalSchedule.Interval.Unit.values().length - 1)];
return new IntervalSchedule.Interval(randomIntBetween(1, 100), unit);
}
}

View File

@ -3,17 +3,17 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.watcher.scheduler.schedule;
package org.elasticsearch.watcher.trigger.schedule;
import com.carrotsearch.randomizedtesting.annotations.Repeat;
import org.elasticsearch.watcher.WatcherSettingsException;
import org.elasticsearch.watcher.scheduler.schedule.support.DayTimes;
import org.elasticsearch.watcher.scheduler.schedule.support.MonthTimes;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.primitives.Ints;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.json.JsonXContent;
import org.elasticsearch.watcher.trigger.schedule.support.DayTimes;
import org.elasticsearch.watcher.trigger.schedule.support.MonthTimes;
import org.junit.Test;
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;

View File

@ -3,7 +3,7 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.watcher.scheduler.schedule;
package org.elasticsearch.watcher.trigger.schedule;
import com.carrotsearch.randomizedtesting.annotations.Repeat;
import org.elasticsearch.common.bytes.BytesReference;
@ -16,7 +16,6 @@ import org.junit.Test;
import java.util.HashMap;
import java.util.Map;
import static org.elasticsearch.watcher.scheduler.schedule.Schedules.cron;
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
import static org.hamcrest.Matchers.*;
@ -49,7 +48,7 @@ public class ScheduleRegistryTests extends ScheduleTestCase {
BytesReference bytes = builder.bytes();
XContentParser parser = JsonXContent.jsonXContent.createParser(bytes);
parser.nextToken();
Schedule schedule = registry.parse(parser);
Schedule schedule = registry.parse("ctx", parser);
assertThat(schedule, notNullValue());
assertThat(schedule, instanceOf(IntervalSchedule.class));
assertThat((IntervalSchedule) schedule, is(interval));
@ -58,8 +57,8 @@ public class ScheduleRegistryTests extends ScheduleTestCase {
@Test @Repeat(iterations = 20)
public void testParse_Cron() throws Exception {
Object cron = randomBoolean() ?
cron("* 0/5 * * * ?") :
cron("* 0/2 * * * ?", "* 0/3 * * * ?", "* 0/5 * * * ?");
Schedules.cron("* 0/5 * * * ?") :
Schedules.cron("* 0/2 * * * ?", "* 0/3 * * * ?", "* 0/5 * * * ?");
XContentBuilder builder = jsonBuilder()
.startObject()
.field(CronSchedule.TYPE, cron)
@ -67,7 +66,7 @@ public class ScheduleRegistryTests extends ScheduleTestCase {
BytesReference bytes = builder.bytes();
XContentParser parser = JsonXContent.jsonXContent.createParser(bytes);
parser.nextToken();
Schedule schedule = registry.parse(parser);
Schedule schedule = registry.parse("ctx", parser);
assertThat(schedule, notNullValue());
assertThat(schedule, instanceOf(CronSchedule.class));
assertThat(schedule, is(cron));
@ -83,7 +82,7 @@ public class ScheduleRegistryTests extends ScheduleTestCase {
BytesReference bytes = builder.bytes();
XContentParser parser = JsonXContent.jsonXContent.createParser(bytes);
parser.nextToken();
Schedule schedule = registry.parse(parser);
Schedule schedule = registry.parse("ctx", parser);
assertThat(schedule, notNullValue());
assertThat(schedule, instanceOf(HourlySchedule.class));
assertThat((HourlySchedule) schedule, equalTo(hourly));
@ -99,7 +98,7 @@ public class ScheduleRegistryTests extends ScheduleTestCase {
BytesReference bytes = builder.bytes();
XContentParser parser = JsonXContent.jsonXContent.createParser(bytes);
parser.nextToken();
Schedule schedule = registry.parse(parser);
Schedule schedule = registry.parse("ctx", parser);
assertThat(schedule, notNullValue());
assertThat(schedule, instanceOf(DailySchedule.class));
assertThat((DailySchedule) schedule, equalTo(daily));
@ -115,7 +114,7 @@ public class ScheduleRegistryTests extends ScheduleTestCase {
BytesReference bytes = builder.bytes();
XContentParser parser = JsonXContent.jsonXContent.createParser(bytes);
parser.nextToken();
Schedule schedule = registry.parse(parser);
Schedule schedule = registry.parse("ctx", parser);
assertThat(schedule, notNullValue());
assertThat(schedule, instanceOf(WeeklySchedule.class));
assertThat((WeeklySchedule) schedule, equalTo(weekly));
@ -131,7 +130,7 @@ public class ScheduleRegistryTests extends ScheduleTestCase {
BytesReference bytes = builder.bytes();
XContentParser parser = JsonXContent.jsonXContent.createParser(bytes);
parser.nextToken();
Schedule schedule = registry.parse(parser);
Schedule schedule = registry.parse("ctx", parser);
assertThat(schedule, notNullValue());
assertThat(schedule, instanceOf(MonthlySchedule.class));
assertThat((MonthlySchedule) schedule, equalTo(monthly));

View File

@ -3,21 +3,20 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.watcher.scheduler.schedule;
package org.elasticsearch.watcher.trigger.schedule;
import org.elasticsearch.common.primitives.Ints;
import org.elasticsearch.watcher.scheduler.schedule.IntervalSchedule.Interval.Unit;
import org.elasticsearch.watcher.scheduler.schedule.support.*;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.test.ElasticsearchTestCase;
import org.elasticsearch.watcher.trigger.schedule.support.*;
import java.io.IOException;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.Set;
import static org.elasticsearch.watcher.scheduler.schedule.Schedules.*;
import static org.elasticsearch.watcher.trigger.schedule.Schedules.*;
/**
*
@ -72,8 +71,8 @@ public abstract class ScheduleTestCase extends ElasticsearchTestCase {
return new IntervalSchedule.Interval(randomIntBetween(1, 100), randomIntervalUnit());
}
protected static Unit randomIntervalUnit() {
return Unit.values()[randomIntBetween(0, Unit.values().length - 1)];
protected static IntervalSchedule.Interval.Unit randomIntervalUnit() {
return IntervalSchedule.Interval.Unit.values()[randomIntBetween(0, IntervalSchedule.Interval.Unit.values().length - 1)];
}
protected static YearTimes validYearTime() {

View File

@ -3,19 +3,19 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.watcher.scheduler.schedule;
package org.elasticsearch.watcher.trigger.schedule;
import com.carrotsearch.randomizedtesting.annotations.Repeat;
import org.elasticsearch.watcher.WatcherSettingsException;
import org.elasticsearch.watcher.scheduler.schedule.support.DayTimes;
import org.elasticsearch.watcher.scheduler.schedule.support.DayOfWeek;
import org.elasticsearch.watcher.scheduler.schedule.support.WeekTimes;
import org.elasticsearch.common.base.Joiner;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.primitives.Ints;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.json.JsonXContent;
import org.elasticsearch.watcher.trigger.schedule.support.DayOfWeek;
import org.elasticsearch.watcher.trigger.schedule.support.DayTimes;
import org.elasticsearch.watcher.trigger.schedule.support.WeekTimes;
import org.junit.Test;
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;

View File

@ -3,18 +3,18 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.watcher.scheduler.schedule;
package org.elasticsearch.watcher.trigger.schedule;
import com.carrotsearch.randomizedtesting.annotations.Repeat;
import org.elasticsearch.watcher.WatcherSettingsException;
import org.elasticsearch.watcher.scheduler.schedule.support.DayTimes;
import org.elasticsearch.watcher.scheduler.schedule.support.YearTimes;
import org.elasticsearch.common.base.Joiner;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.primitives.Ints;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.json.JsonXContent;
import org.elasticsearch.watcher.trigger.schedule.support.DayTimes;
import org.elasticsearch.watcher.trigger.schedule.support.YearTimes;
import org.junit.Test;
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;

View File

@ -3,20 +3,25 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.watcher.scheduler;
package org.elasticsearch.watcher.trigger.schedule.quartz;
import org.apache.lucene.util.LuceneTestCase.Slow;
import org.elasticsearch.watcher.WatcherPlugin;
import org.elasticsearch.watcher.scheduler.schedule.Schedule;
import org.elasticsearch.watcher.scheduler.schedule.support.DayOfWeek;
import org.elasticsearch.watcher.scheduler.schedule.support.WeekTimes;
import org.elasticsearch.watcher.support.clock.SystemClock;
import org.elasticsearch.common.joda.time.DateTime;
import org.elasticsearch.common.joda.time.DateTimeZone;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.test.ElasticsearchTestCase;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.watcher.WatcherPlugin;
import org.elasticsearch.watcher.support.clock.SystemClock;
import org.elasticsearch.watcher.trigger.Trigger;
import org.elasticsearch.watcher.trigger.TriggerEngine;
import org.elasticsearch.watcher.trigger.TriggerEvent;
import org.elasticsearch.watcher.trigger.schedule.Schedule;
import org.elasticsearch.watcher.trigger.schedule.ScheduleRegistry;
import org.elasticsearch.watcher.trigger.schedule.ScheduleTrigger;
import org.elasticsearch.watcher.trigger.schedule.support.DayOfWeek;
import org.elasticsearch.watcher.trigger.schedule.support.WeekTimes;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@ -28,17 +33,18 @@ import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import static org.elasticsearch.watcher.scheduler.schedule.Schedules.*;
import static org.elasticsearch.watcher.trigger.schedule.Schedules.*;
import static org.hamcrest.Matchers.is;
import static org.mockito.Mockito.mock;
/**
*
*/
@Slow
public class InternalSchedulerTests extends ElasticsearchTestCase {
public class QuartzScheduleEngineTests extends ElasticsearchTestCase {
private ThreadPool threadPool;
private InternalScheduler scheduler;
private QuartzScheduleTriggerEngine engine;
@Before
public void init() throws Exception {
@ -48,12 +54,12 @@ public class InternalSchedulerTests extends ElasticsearchTestCase {
.put("name", "test")
.build();
threadPool = new ThreadPool(settings, null);
scheduler = new InternalScheduler(ImmutableSettings.EMPTY, threadPool, SystemClock.INSTANCE);
engine = new QuartzScheduleTriggerEngine(ImmutableSettings.EMPTY, mock(ScheduleRegistry.class), threadPool, SystemClock.INSTANCE);
}
@After
public void cleanup() throws Exception {
scheduler.stop();
engine.stop();
threadPool.shutdownNow();
}
@ -61,14 +67,14 @@ public class InternalSchedulerTests extends ElasticsearchTestCase {
public void testStart() throws Exception {
int count = randomIntBetween(2, 5);
final CountDownLatch latch = new CountDownLatch(count);
List<Scheduler.Job> jobs = new ArrayList<>();
List<TriggerEngine.Job> jobs = new ArrayList<>();
for (int i = 0; i < count; i++) {
jobs.add(new SimpleJob(String.valueOf(i), interval("3s")));
}
final BitSet bits = new BitSet(count);
scheduler.addListener(new Scheduler.Listener() {
engine.register(new TriggerEngine.Listener() {
@Override
public void fire(String jobName, DateTime scheduledFireTime, DateTime fireTime) {
public void triggered(String jobName, TriggerEvent event) {
int index = Integer.parseInt(jobName);
if (!bits.get(index)) {
logger.info("job [" + index + "] first fire: " + new DateTime());
@ -79,11 +85,11 @@ public class InternalSchedulerTests extends ElasticsearchTestCase {
}
}
});
scheduler.start(jobs);
engine.start(jobs);
if (!latch.await(5, TimeUnit.SECONDS)) {
fail("waiting too long for all watches to be triggered");
}
scheduler.stop();
engine.stop();
assertThat(bits.cardinality(), is(count));
}
@ -91,10 +97,10 @@ public class InternalSchedulerTests extends ElasticsearchTestCase {
public void testAdd_Hourly() throws Exception {
final String name = "job_name";
final CountDownLatch latch = new CountDownLatch(1);
scheduler.start(Collections.<Scheduler.Job>emptySet());
scheduler.addListener(new Scheduler.Listener() {
engine.start(Collections.<TriggerEngine.Job>emptySet());
engine.register(new TriggerEngine.Listener() {
@Override
public void fire(String jobName, DateTime scheduledFireTime, DateTime fireTime) {
public void triggered(String jobName, TriggerEvent event) {
assertThat(jobName, is(name));
logger.info("triggered job on [{}]", new DateTime());
latch.countDown();
@ -110,7 +116,7 @@ public class InternalSchedulerTests extends ElasticsearchTestCase {
int minute = minOfHour.value;
logger.info("scheduling hourly job [{}]", minute);
logger.info("current date [{}]", now);
scheduler.add(new SimpleJob(name, hourly(minute)));
engine.add(new SimpleJob(name, hourly(minute)));
long secondsToWait = now.getSecondOfMinute() < 29 ? 62 - now.getSecondOfMinute() : 122 - now.getSecondOfMinute();
logger.info("waiting at least [{}] seconds for response", secondsToWait);
if (!latch.await(secondsToWait, TimeUnit.SECONDS)) {
@ -122,10 +128,10 @@ public class InternalSchedulerTests extends ElasticsearchTestCase {
public void testAdd_Daily() throws Exception {
final String name = "job_name";
final CountDownLatch latch = new CountDownLatch(1);
scheduler.start(Collections.<Scheduler.Job>emptySet());
scheduler.addListener(new Scheduler.Listener() {
engine.start(Collections.<TriggerEngine.Job>emptySet());
engine.register(new TriggerEngine.Listener() {
@Override
public void fire(String jobName, DateTime scheduledFireTime, DateTime fireTime) {
public void triggered(String jobName, TriggerEvent event) {
assertThat(jobName, is(name));
logger.info("triggered job on [{}]", new DateTime());
latch.countDown();
@ -142,7 +148,7 @@ public class InternalSchedulerTests extends ElasticsearchTestCase {
int hour = hourOfDay.value;
logger.info("scheduling hourly job [{}:{}]", hour, minute);
logger.info("current date [{}]", now);
scheduler.add(new SimpleJob(name, daily().at(hour, minute).build()));
engine.add(new SimpleJob(name, daily().at(hour, minute).build()));
// 30 sec is the default idle time of quartz
long secondsToWait = now.getSecondOfMinute() < 29 ? 62 - now.getSecondOfMinute() : 122 - now.getSecondOfMinute();
logger.info("waiting at least [{}] seconds for response", secondsToWait);
@ -155,10 +161,10 @@ public class InternalSchedulerTests extends ElasticsearchTestCase {
public void testAdd_Weekly() throws Exception {
final String name = "job_name";
final CountDownLatch latch = new CountDownLatch(1);
scheduler.start(Collections.<Scheduler.Job>emptySet());
scheduler.addListener(new Scheduler.Listener() {
engine.start(Collections.<TriggerEngine.Job>emptySet());
engine.register(new TriggerEngine.Listener() {
@Override
public void fire(String jobName, DateTime scheduledFireTime, DateTime fireTime) {
public void triggered(String jobName, TriggerEvent event) {
assertThat(jobName, is(name));
logger.info("triggered job on [{}]", new DateTime());
latch.countDown();
@ -177,7 +183,7 @@ public class InternalSchedulerTests extends ElasticsearchTestCase {
DayOfWeek day = dayOfWeek.day();
logger.info("scheduling hourly job [{} {}:{}]", day, hour, minute);
logger.info("current date [{}]", now);
scheduler.add(new SimpleJob(name, weekly().time(WeekTimes.builder().on(day).at(hour, minute).build()).build()));
engine.add(new SimpleJob(name, weekly().time(WeekTimes.builder().on(day).at(hour, minute).build()).build()));
// 30 sec is the default idle time of quartz
long secondsToWait = now.getSecondOfMinute() < 29 ? 62 - now.getSecondOfMinute() : 122 - now.getSecondOfMinute();
logger.info("waiting at least [{}] seconds for response", secondsToWait);
@ -186,14 +192,14 @@ public class InternalSchedulerTests extends ElasticsearchTestCase {
}
}
static class SimpleJob implements Scheduler.Job {
static class SimpleJob implements TriggerEngine.Job {
private final String name;
private final Schedule schedule;
private final ScheduleTrigger trigger;
public SimpleJob(String name, Schedule schedule) {
this.name = name;
this.schedule = schedule;
this.trigger = new ScheduleTrigger(schedule);
}
@Override
@ -202,8 +208,8 @@ public class InternalSchedulerTests extends ElasticsearchTestCase {
}
@Override
public Schedule schedule() {
return schedule;
public Trigger trigger() {
return trigger;
}
}

View File

@ -7,14 +7,15 @@ package org.elasticsearch.watcher.watch;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.watcher.WatcherException;
import org.elasticsearch.watcher.history.HistoryService;
import org.elasticsearch.watcher.scheduler.Scheduler;
import org.elasticsearch.watcher.scheduler.schedule.Schedule;
import org.elasticsearch.common.bytes.BytesArray;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.test.ElasticsearchTestCase;
import org.elasticsearch.watcher.WatcherException;
import org.elasticsearch.watcher.history.HistoryService;
import org.elasticsearch.watcher.trigger.Trigger;
import org.elasticsearch.watcher.trigger.TriggerEngine;
import org.elasticsearch.watcher.trigger.TriggerService;
import org.junit.Before;
import org.junit.Test;
@ -30,7 +31,7 @@ import static org.mockito.Mockito.*;
*/
public class WatchServiceTests extends ElasticsearchTestCase {
private Scheduler scheduler;
private TriggerService triggerService;
private WatchStore watchStore;
private WatchService watchService;
private HistoryService historyService;
@ -38,11 +39,11 @@ public class WatchServiceTests extends ElasticsearchTestCase {
@Before
public void init() throws Exception {
scheduler = mock(Scheduler.class);
triggerService = mock(TriggerService.class);
watchStore = mock(WatchStore.class);
historyService = mock(HistoryService.class);
watchLockService = mock(WatchLockService.class);
watchService = new WatchService(ImmutableSettings.EMPTY, scheduler, watchStore, historyService, watchLockService);
watchService = new WatchService(ImmutableSettings.EMPTY, triggerService, watchStore, historyService, watchLockService);
Field field = WatchService.class.getDeclaredField("state");
field.setAccessible(true);
AtomicReference<WatchService.State> state = (AtomicReference<WatchService.State>) field.get(watchService);
@ -63,21 +64,21 @@ public class WatchServiceTests extends ElasticsearchTestCase {
IndexResponse response = watchService.putWatch("_name", new BytesArray("{}"));
assertThat(response, sameInstance(indexResponse));
verify(scheduler, times(1)).add(any(Scheduler.Job.class));
verify(triggerService, times(1)).add(any(TriggerEngine.Job.class));
}
@Test
public void testPutWatch_NotSchedule() {
Schedule schedule = mock(Schedule.class);
Trigger trigger = mock(Trigger.class);
IndexResponse indexResponse = mock(IndexResponse.class);
Watch watch = mock(Watch.class);
when(watch.schedule()).thenReturn(schedule);
when(watch.trigger()).thenReturn(trigger);
WatchStore.WatchPut watchPut = mock(WatchStore.WatchPut.class);
when(watchPut.indexResponse()).thenReturn(indexResponse);
when(watchPut.current()).thenReturn(watch);
Watch previousWatch = mock(Watch.class);
when(previousWatch.schedule()).thenReturn(schedule);
when(previousWatch.trigger()).thenReturn(trigger);
when(watchPut.previous()).thenReturn(previousWatch);
WatchLockService.Lock lock = mock(WatchLockService.Lock.class);
@ -86,7 +87,7 @@ public class WatchServiceTests extends ElasticsearchTestCase {
IndexResponse response = watchService.putWatch("_name", new BytesArray("{}"));
assertThat(response, sameInstance(indexResponse));
verifyZeroInteractions(scheduler);
verifyZeroInteractions(triggerService);
}
@Test
@ -102,7 +103,7 @@ public class WatchServiceTests extends ElasticsearchTestCase {
WatchStore.WatchDelete watchDelete = watchService.deleteWatch("_name");
assertThat(watchDelete, sameInstance(expectedWatchDelete));
verify(scheduler, times(1)).remove("_name");
verify(triggerService, times(1)).remove("_name");
}
@Test
@ -118,7 +119,7 @@ public class WatchServiceTests extends ElasticsearchTestCase {
WatchStore.WatchDelete watchDelete = watchService.deleteWatch("_name");
assertThat(watchDelete, sameInstance(expectedWatchDelete));
verifyZeroInteractions(scheduler);
verifyZeroInteractions(triggerService);
}
@Test

View File

@ -5,7 +5,20 @@
*/
package org.elasticsearch.watcher.watch;
import com.carrotsearch.ant.tasks.junit4.dependencies.com.google.common.collect.ImmutableSet;
import com.carrotsearch.randomizedtesting.annotations.Repeat;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.collect.ImmutableList;
import org.elasticsearch.common.collect.ImmutableMap;
import org.elasticsearch.common.logging.ESLogger;
import org.elasticsearch.common.logging.Loggers;
import org.elasticsearch.common.netty.handler.codec.http.HttpMethod;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.test.ElasticsearchTestCase;
import org.elasticsearch.watcher.actions.Action;
import org.elasticsearch.watcher.actions.ActionRegistry;
import org.elasticsearch.watcher.actions.Actions;
@ -24,31 +37,25 @@ import org.elasticsearch.watcher.input.Input;
import org.elasticsearch.watcher.input.InputRegistry;
import org.elasticsearch.watcher.input.search.SearchInput;
import org.elasticsearch.watcher.input.simple.SimpleInput;
import org.elasticsearch.watcher.scheduler.schedule.*;
import org.elasticsearch.watcher.scheduler.schedule.support.*;
import org.elasticsearch.watcher.support.WatcherUtils;
import org.elasticsearch.watcher.support.Script;
import org.elasticsearch.watcher.support.WatcherUtils;
import org.elasticsearch.watcher.support.clock.SystemClock;
import org.elasticsearch.watcher.support.init.proxy.ClientProxy;
import org.elasticsearch.watcher.support.init.proxy.ScriptServiceProxy;
import org.elasticsearch.watcher.support.template.ScriptTemplate;
import org.elasticsearch.watcher.support.template.Template;
import org.elasticsearch.watcher.test.WatcherTestUtils;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.collect.ImmutableList;
import org.elasticsearch.common.collect.ImmutableMap;
import org.elasticsearch.common.logging.ESLogger;
import org.elasticsearch.common.logging.Loggers;
import org.elasticsearch.common.netty.handler.codec.http.HttpMethod;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.test.ElasticsearchTestCase;
import org.elasticsearch.watcher.transform.*;
import org.elasticsearch.watcher.trigger.Trigger;
import org.elasticsearch.watcher.trigger.TriggerEngine;
import org.elasticsearch.watcher.trigger.TriggerService;
import org.elasticsearch.watcher.trigger.schedule.*;
import org.elasticsearch.watcher.trigger.schedule.support.*;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.util.Collection;
import java.util.Map;
import static org.elasticsearch.watcher.test.WatcherTestUtils.matchAllRequest;
@ -81,7 +88,10 @@ public class WatchTests extends ElasticsearchTestCase {
TransformRegistry transformRegistry = transformRegistry();
Schedule schedule = randomSchedule();
Trigger trigger = new ScheduleTrigger(schedule);
ScheduleRegistry scheduleRegistry = registry(schedule);
TriggerEngine triggerEngine = new ParseOnlyScheduleTriggerEngine(ImmutableSettings.EMPTY, scheduleRegistry);
TriggerService triggerService = new TriggerService(ImmutableSettings.EMPTY, ImmutableSet.of(triggerEngine));
Input input = randomInput();
InputRegistry inputRegistry = registry(input);
@ -100,11 +110,11 @@ public class WatchTests extends ElasticsearchTestCase {
TimeValue throttlePeriod = randomBoolean() ? null : TimeValue.timeValueSeconds(randomIntBetween(5, 10));
Watch watch = new Watch("_name", SystemClock.INSTANCE, schedule, input, condition, transform, actions, metadata, throttlePeriod, status);
Watch watch = new Watch("_name", SystemClock.INSTANCE, trigger, input, condition, transform, actions, metadata, throttlePeriod, status);
BytesReference bytes = XContentFactory.jsonBuilder().value(watch).bytes();
logger.info(bytes.toUtf8());
Watch.Parser watchParser = new Watch.Parser(settings, conditionRegistry, scheduleRegistry, transformRegistry, actionRegistry, inputRegistry, SystemClock.INSTANCE);
Watch.Parser watchParser = new Watch.Parser(settings, conditionRegistry, triggerService, transformRegistry, actionRegistry, inputRegistry, SystemClock.INSTANCE);
boolean includeStatus = randomBoolean();
Watch parsedWatch = watchParser.parse("_name", includeStatus, bytes);
@ -112,7 +122,7 @@ public class WatchTests extends ElasticsearchTestCase {
if (includeStatus) {
assertThat(parsedWatch.status(), equalTo(status));
}
assertThat(parsedWatch.schedule(), equalTo(schedule));
assertThat(parsedWatch.trigger(), equalTo(trigger));
assertThat(parsedWatch.input(), equalTo(input));
assertThat(parsedWatch.condition(), equalTo(condition));
if (throttlePeriod != null) {
@ -273,4 +283,46 @@ public class WatchTests extends ElasticsearchTestCase {
return new ActionRegistry(parsers.build());
}
static class ParseOnlyScheduleTriggerEngine extends ScheduleTriggerEngine {
private final ScheduleRegistry registry;
public ParseOnlyScheduleTriggerEngine(Settings settings, ScheduleRegistry registry) {
super(settings);
this.registry = registry;
}
@Override
public void start(Collection<Job> jobs) {
}
@Override
public void stop() {
}
@Override
public void register(Listener listener) {
}
@Override
public void add(Job job) {
}
@Override
public boolean remove(String jobName) {
return false;
}
@Override
public ScheduleTrigger parseTrigger(String context, XContentParser parser) throws IOException {
Schedule schedule = registry.parse(context, parser);
return new ScheduleTrigger(schedule);
}
@Override
public ScheduleTriggerEvent parseTriggerEvent(String context, XContentParser parser) throws IOException {
return null;
}
}
}