diff --git a/src/main/java/org/elasticsearch/alerts/Alert.java b/src/main/java/org/elasticsearch/alerts/Alert.java index bf4255c3b6b..6d52cf9fa79 100644 --- a/src/main/java/org/elasticsearch/alerts/Alert.java +++ b/src/main/java/org/elasticsearch/alerts/Alert.java @@ -17,7 +17,7 @@ import java.util.List; public class Alert implements ToXContent { - private final String alertName; + private String alertName; private String queryName; private AlertTrigger trigger; private TimeValue timePeriod; @@ -93,6 +93,10 @@ public class Alert implements ToXContent { return alertName; } + public void alertName(String alertName) { + this.alertName = alertName; + } + public String queryName() { return queryName; } @@ -141,6 +145,9 @@ public class Alert implements ToXContent { this.lastRan = lastRan; } + public Alert() { + } + public Alert(String alertName, String queryName, AlertTrigger trigger, TimeValue timePeriod, List actions, String schedule, DateTime lastRan, List indices, DateTime running, long version, boolean enabled, boolean simpleQuery){ @@ -163,35 +170,49 @@ public class Alert implements ToXContent { //Note we deliberately don't serialize the version here builder.startObject(); - builder.field(AlertsStore.QUERY_FIELD.getPreferredName(), queryName); - builder.field(AlertsStore.SCHEDULE_FIELD.getPreferredName(), schedule); - builder.field(AlertsStore.TIMEPERIOD_FIELD.getPreferredName(), timePeriod); - builder.field(AlertsStore.LASTRAN_FIELD.getPreferredName(), lastRan); - builder.field(AlertsStore.CURRENTLY_RUNNING.getPreferredName(), running); + if (queryName != null) { + builder.field(AlertsStore.QUERY_NAME_FIELD.getPreferredName(), queryName); + } + if (schedule != null) { + builder.field(AlertsStore.SCHEDULE_FIELD.getPreferredName(), schedule); + } + if (timePeriod != null) { + builder.field(AlertsStore.TIMEPERIOD_FIELD.getPreferredName(), timePeriod); + } + if (lastRan != null) { + builder.field(AlertsStore.LASTRAN_FIELD.getPreferredName(), lastRan); + } + if (running != null) { + builder.field(AlertsStore.CURRENTLY_RUNNING.getPreferredName(), running); + } builder.field(AlertsStore.ENABLED.getPreferredName(), enabled); builder.field(AlertsStore.SIMPLE_QUERY.getPreferredName(), simpleQuery); - builder.field(AlertsStore.LAST_ACTION_FIRE.getPreferredName(), lastActionFire); - - builder.field(AlertsStore.TRIGGER_FIELD.getPreferredName()); - trigger.toXContent(builder, params); - builder.field(AlertsStore.ACTION_FIELD.getPreferredName()); - - builder.startObject(); - for (AlertAction action : actions){ - builder.field(action.getActionName()); - action.toXContent(builder, params); + if (lastActionFire != null) { + builder.field(AlertsStore.LAST_ACTION_FIRE.getPreferredName(), lastActionFire); + } + + if (actions != null && !actions.isEmpty()) { + builder.startObject(AlertsStore.ACTION_FIELD.getPreferredName()); + for (AlertAction action : actions){ + builder.field(action.getActionName()); + action.toXContent(builder, params); + } + builder.endObject(); } - builder.endObject(); if (indices != null && !indices.isEmpty()) { - builder.field(AlertsStore.INDICES.getPreferredName()); - builder.startArray(); + builder.startArray(AlertsStore.INDICES.getPreferredName()); for (String index : indices){ builder.value(index); } builder.endArray(); } + if (trigger != null) { + builder.field(AlertsStore.TRIGGER_FIELD.getPreferredName()); + trigger.toXContent(builder, params); + } + builder.endObject(); return builder; } diff --git a/src/main/java/org/elasticsearch/alerts/AlertsStore.java b/src/main/java/org/elasticsearch/alerts/AlertsStore.java index 6100211e93d..d93d07c4eba 100644 --- a/src/main/java/org/elasticsearch/alerts/AlertsStore.java +++ b/src/main/java/org/elasticsearch/alerts/AlertsStore.java @@ -17,7 +17,6 @@ import org.elasticsearch.action.index.IndexResponse; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.alerts.actions.AlertAction; import org.elasticsearch.alerts.actions.AlertActionRegistry; -import org.elasticsearch.alerts.triggers.AlertTrigger; import org.elasticsearch.alerts.triggers.TriggerManager; import org.elasticsearch.client.Client; import org.elasticsearch.client.Requests; @@ -30,24 +29,19 @@ import org.elasticsearch.common.joda.time.DateTime; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.util.concurrent.ConcurrentCollections; -import org.elasticsearch.common.xcontent.ToXContent; -import org.elasticsearch.common.xcontent.XContentBuilder; -import org.elasticsearch.common.xcontent.XContentFactory; -import org.elasticsearch.common.xcontent.XContentHelper; +import org.elasticsearch.common.xcontent.*; import org.elasticsearch.search.SearchHit; import java.io.IOException; import java.util.ArrayList; import java.util.List; -import java.util.Locale; -import java.util.Map; import java.util.concurrent.ConcurrentMap; /** */ public class AlertsStore extends AbstractComponent { - public static final ParseField QUERY_FIELD = new ParseField("query"); + public static final ParseField QUERY_NAME_FIELD = new ParseField("query"); public static final ParseField SCHEDULE_FIELD = new ParseField("schedule"); public static final ParseField TRIGGER_FIELD = new ParseField("trigger"); public static final ParseField TIMEPERIOD_FIELD = new ParseField("timeperiod"); @@ -200,96 +194,73 @@ public class AlertsStore extends AbstractComponent { return parseAlert(alertId, sh.getSourceRef(), sh.getVersion()); } - private Alert parseAlert(String alertId, BytesReference bytesReference, long version) { - // TODO: streaming parsing! - Map fields = XContentHelper.convertToMap(bytesReference, false).v2(); - String query = fields.get(QUERY_FIELD.getPreferredName()).toString(); - String schedule = fields.get(SCHEDULE_FIELD.getPreferredName()).toString(); - Object triggerObj = fields.get(TRIGGER_FIELD.getPreferredName()); - AlertTrigger trigger = null; - if (triggerObj instanceof Map) { - Map triggerMap = (Map) triggerObj; - trigger = TriggerManager.parseTriggerFromMap(triggerMap); - } else { - throw new ElasticsearchException("Unable to parse trigger [" + triggerObj + "]"); - } - - String timeString = fields.get(TIMEPERIOD_FIELD.getPreferredName()).toString(); - TimeValue timePeriod = TimeValue.parseTimeValue(timeString, defaultTimePeriod); - - Object actionObj = fields.get(ACTION_FIELD.getPreferredName()); - List actions = null; - if (actionObj instanceof Map) { - Map actionMap = (Map) actionObj; - actions = alertActionRegistry.parseActionsFromMap(actionMap); - } else { - throw new ElasticsearchException("Unable to parse actions [" + actionObj + "]"); - } - - DateTime lastRan = new DateTime(0); - if( fields.get(LASTRAN_FIELD.getPreferredName()) != null){ - lastRan = new DateTime(fields.get(LASTRAN_FIELD.getPreferredName()).toString()); - } else if (fields.get("lastRan") != null) { - lastRan = new DateTime(fields.get("lastRan").toString()); - } - - DateTime running = new DateTime(0); - if (fields.get(CURRENTLY_RUNNING.getPreferredName()) != null) { - running = new DateTime(fields.get(CURRENTLY_RUNNING.getPreferredName()).toString()); - } - - DateTime lastActionFire = new DateTime(0); - if (fields.get(LAST_ACTION_FIRE.getPreferredName()) != null) { - lastActionFire = new DateTime(fields.get(LAST_ACTION_FIRE.getPreferredName()).toString()); - } - - List indices = new ArrayList<>(); - if (fields.get(INDICES.getPreferredName()) != null && fields.get(INDICES.getPreferredName()) instanceof List){ - indices = (List)fields.get(INDICES.getPreferredName()); - } else { - logger.warn("Indices : " + fields.get(INDICES.getPreferredName()) + " class " + fields.get(INDICES.getPreferredName()).getClass() ); - } - - boolean enabled = true; - if (fields.get(ENABLED.getPreferredName()) != null ) { - logger.error(ENABLED.getPreferredName() + " " + fields.get(ENABLED.getPreferredName())); - Object enabledObj = fields.get(ENABLED.getPreferredName()); - enabled = parseAsBoolean(enabledObj); - } - - boolean simpleQuery = true; - if (fields.get(SIMPLE_QUERY.getPreferredName()) != null ) { - logger.error(SIMPLE_QUERY.getPreferredName() + " " + fields.get(SIMPLE_QUERY.getPreferredName())); - Object enabledObj = fields.get(SIMPLE_QUERY.getPreferredName()); - simpleQuery = parseAsBoolean(enabledObj); - } - - Alert alert = new Alert(alertId, query, trigger, timePeriod, actions, schedule, lastRan, indices, running, version, enabled, simpleQuery); - alert.lastActionFire(lastActionFire); - - if (fields.get(TIMESTAMP_FIELD.getPreferredName()) != null) { - alert.timestampString(fields.get(TIMESTAMP_FIELD.getPreferredName()).toString()); - } - - return alert; - } - - private boolean parseAsBoolean(Object enabledObj) { - boolean enabled; - if (enabledObj instanceof Boolean){ - enabled = (Boolean)enabledObj; - } else { - if (enabledObj.toString().toLowerCase(Locale.ROOT).equals("true") || - enabledObj.toString().toLowerCase(Locale.ROOT).equals("1")) { - enabled = true; - } else if ( enabledObj.toString().toLowerCase(Locale.ROOT).equals("false") || - enabledObj.toString().toLowerCase(Locale.ROOT).equals("0")) { - enabled = false; - } else { - throw new ElasticsearchIllegalArgumentException("Unable to parse [" + enabledObj + "] as a boolean"); + private Alert parseAlert(String alertName, BytesReference source, long version) { + Alert alert = new Alert(); + alert.alertName(alertName); + alert.version(version); + try (XContentParser parser = XContentHelper.createParser(source)) { + String currentFieldName = null; + XContentParser.Token token = parser.nextToken(); + assert token == XContentParser.Token.START_OBJECT; + while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { + if (token == XContentParser.Token.FIELD_NAME) { + currentFieldName = parser.currentName(); + } else if (token == XContentParser.Token.START_OBJECT) { + if (TRIGGER_FIELD.match(currentFieldName)) { + alert.trigger(TriggerManager.parseTrigger(parser)); + } else if (ACTION_FIELD.match(currentFieldName)) { + List actions = alertActionRegistry.instantiateAlertActions(parser); + alert.actions(actions); + } else { + throw new ElasticsearchIllegalArgumentException("Unexpected field [" + currentFieldName + "]"); + } + } else if (token == XContentParser.Token.START_ARRAY) { + if (INDICES.match(currentFieldName)) { + List indices = new ArrayList<>(); + while (parser.nextToken() != XContentParser.Token.END_ARRAY) { + indices.add(parser.text()); + } + alert.indices(indices); + } else { + throw new ElasticsearchIllegalArgumentException("Unexpected field [" + currentFieldName + "]"); + } + } else if (token.isValue()) { + if (QUERY_NAME_FIELD.match(currentFieldName)) { + alert.queryName(parser.textOrNull()); + } else if (SCHEDULE_FIELD.match(currentFieldName)) { + alert.schedule(parser.textOrNull()); + } else if (TIMEPERIOD_FIELD.match(currentFieldName)) { + alert.timestampString(parser.textOrNull()); + } else if (LASTRAN_FIELD.match(currentFieldName)) { + alert.lastRan(DateTime.parse(parser.textOrNull())); + } else if (CURRENTLY_RUNNING.match(currentFieldName)) { + alert.running(DateTime.parse(parser.textOrNull())); + } else if (ENABLED.match(currentFieldName)) { + alert.enabled(parser.booleanValue()); + } else if (SIMPLE_QUERY.match(currentFieldName)) { + alert.simpleQuery(parser.booleanValue()); + } else if (TIMEPERIOD_FIELD.match(currentFieldName)) { + alert.timePeriod(TimeValue.parseTimeValue(parser.textOrNull(), defaultTimePeriod)); + } else if (LAST_ACTION_FIRE.match(currentFieldName)) { + alert.lastActionFire(DateTime.parse(parser.textOrNull())); + } else { + throw new ElasticsearchIllegalArgumentException("Unexpected field [" + currentFieldName + "]"); + } + } else { + throw new ElasticsearchIllegalArgumentException("Unexpected token [" + token + "]"); + } } + } catch (IOException e) { + throw new ElasticsearchException("Error during parsing alert", e); } - return enabled; + + if (alert.timePeriod() == null) { + alert.timePeriod(defaultTimePeriod); + } + if (alert.lastActionFire() == null) { + alert.lastActionFire(new DateTime(0)); + } + return alert; } private ClusterHealthStatus createAlertsIndex() { diff --git a/src/main/java/org/elasticsearch/alerts/actions/AlertAction.java b/src/main/java/org/elasticsearch/alerts/actions/AlertAction.java index d437b83b6fb..6ec15307b6b 100644 --- a/src/main/java/org/elasticsearch/alerts/actions/AlertAction.java +++ b/src/main/java/org/elasticsearch/alerts/actions/AlertAction.java @@ -12,7 +12,9 @@ import org.elasticsearch.common.xcontent.XContentBuilder; import java.io.IOException; public interface AlertAction extends ToXContent { + public String getActionName(); + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException; public boolean doAction(Alert alert, AlertActionEntry actionEntry); diff --git a/src/main/java/org/elasticsearch/alerts/actions/AlertActionFactory.java b/src/main/java/org/elasticsearch/alerts/actions/AlertActionFactory.java index 19920458524..7452bd7eb7f 100644 --- a/src/main/java/org/elasticsearch/alerts/actions/AlertActionFactory.java +++ b/src/main/java/org/elasticsearch/alerts/actions/AlertActionFactory.java @@ -5,6 +5,12 @@ */ package org.elasticsearch.alerts.actions; +import org.elasticsearch.common.xcontent.XContentParser; + +import java.io.IOException; + public interface AlertActionFactory { - AlertAction createAction(Object parameters); + + AlertAction createAction(XContentParser parser) throws IOException; + } diff --git a/src/main/java/org/elasticsearch/alerts/actions/AlertActionManager.java b/src/main/java/org/elasticsearch/alerts/actions/AlertActionManager.java index 398fddc9088..0c9fa6763b1 100644 --- a/src/main/java/org/elasticsearch/alerts/actions/AlertActionManager.java +++ b/src/main/java/org/elasticsearch/alerts/actions/AlertActionManager.java @@ -6,6 +6,7 @@ package org.elasticsearch.alerts.actions; import org.elasticsearch.ElasticsearchException; +import org.elasticsearch.ElasticsearchIllegalArgumentException; import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse; import org.elasticsearch.action.admin.cluster.health.ClusterHealthStatus; import org.elasticsearch.action.admin.indices.create.CreateIndexResponse; @@ -15,22 +16,18 @@ import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.action.search.SearchRequestBuilder; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.action.update.UpdateRequest; -import org.elasticsearch.action.update.UpdateResponse; import org.elasticsearch.alerts.AlertManager; import org.elasticsearch.alerts.triggers.AlertTrigger; import org.elasticsearch.alerts.triggers.TriggerManager; import org.elasticsearch.client.Client; import org.elasticsearch.client.Requests; -import org.elasticsearch.cluster.ClusterService; import org.elasticsearch.common.Nullable; import org.elasticsearch.common.Priority; +import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.joda.time.DateTime; import org.elasticsearch.common.logging.ESLogger; import org.elasticsearch.common.logging.Loggers; -import org.elasticsearch.common.unit.TimeValue; -import org.elasticsearch.common.xcontent.ToXContent; -import org.elasticsearch.common.xcontent.XContentBuilder; -import org.elasticsearch.common.xcontent.XContentFactory; +import org.elasticsearch.common.xcontent.*; import org.elasticsearch.index.engine.DocumentAlreadyExistsException; import org.elasticsearch.search.SearchHit; import org.elasticsearch.threadpool.ThreadPool; @@ -38,11 +35,9 @@ import org.elasticsearch.threadpool.ThreadPool; import java.io.IOException; import java.util.ArrayList; import java.util.List; -import java.util.Map; import java.util.concurrent.BlockingQueue; import java.util.concurrent.Executor; import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; /** @@ -197,45 +192,82 @@ public class AlertActionManager { protected AlertActionEntry parseHistory(String historyId, SearchHit sh, long version) { - Map fields = sh.sourceAsMap(); - return parseHistory(historyId, fields, version); + return parseHistory(historyId, sh.getSourceRef(), version); } - protected AlertActionEntry parseHistory(String historyId, Map fields, long version) { - return parseHistory(historyId, fields, version, actionRegistry, logger); + protected AlertActionEntry parseHistory(String historyId, BytesReference source, long version) { + return parseHistory(historyId, source, version, actionRegistry, logger); } - protected static AlertActionEntry parseHistory(String historyId, Map fields, long version, + protected static AlertActionEntry parseHistory(String historyId, BytesReference source, long version, AlertActionRegistry actionRegistry, ESLogger logger) { - String alertName = fields.get(ALERT_NAME_FIELD).toString(); - boolean triggered = (Boolean)fields.get(TRIGGERED_FIELD); - DateTime fireTime = new DateTime(fields.get(FIRE_TIME_FIELD).toString()); - DateTime scheduledFireTime = new DateTime(fields.get(SCHEDULED_FIRE_TIME_FIELD).toString()); - AlertTrigger trigger = TriggerManager.parseTriggerFromMap((Map)fields.get(TRIGGER_FIELD)); - String queryRan = fields.get(QUERY_RAN_FIELD).toString(); - long numberOfResults = ((Number)fields.get(NUMBER_OF_RESULTS_FIELD)).longValue(); - Object actionObj = fields.get(ACTIONS_FIELD); - List actions; - if (actionObj instanceof Map) { - Map actionMap = (Map) actionObj; - actions = actionRegistry.parseActionsFromMap(actionMap); - } else { - throw new ElasticsearchException("Unable to parse actions [" + actionObj + "]"); + AlertActionEntry entry = new AlertActionEntry(); + entry.setId(historyId); + entry.setVersion(version); + try (XContentParser parser = XContentHelper.createParser(source)) { + String currentFieldName = null; + XContentParser.Token token = parser.nextToken(); + assert token == XContentParser.Token.START_OBJECT; + while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { + if (token == XContentParser.Token.FIELD_NAME) { + currentFieldName = parser.currentName(); + } else if (token == XContentParser.Token.START_OBJECT) { + switch (currentFieldName) { + case ACTIONS_FIELD: + entry.setActions(actionRegistry.instantiateAlertActions(parser)); + break; + case TRIGGER_FIELD: + entry.setTrigger(TriggerManager.parseTrigger(parser)); + break; + default: + throw new ElasticsearchIllegalArgumentException("Unexpected field [" + currentFieldName + "]"); + } + } else if (token == XContentParser.Token.START_ARRAY) { + switch (currentFieldName) { + case INDICES_FIELD: + List indices = new ArrayList<>(); + while (parser.nextToken() != XContentParser.Token.END_ARRAY) { + indices.add(parser.text()); + } + entry.setIndices(indices); + break; + default: + throw new ElasticsearchIllegalArgumentException("Unexpected field [" + currentFieldName + "]"); + } + } else if (token.isValue()) { + switch (currentFieldName) { + case ALERT_NAME_FIELD: + entry.setAlertName(parser.text()); + break; + case TRIGGERED_FIELD: + entry.setTriggered(parser.booleanValue()); + break; + case FIRE_TIME_FIELD: + entry.setFireTime(DateTime.parse(parser.text())); + break; + case SCHEDULED_FIRE_TIME_FIELD: + entry.setScheduledTime(DateTime.parse(parser.text())); + break; + case QUERY_RAN_FIELD: + entry.setTriggeringQuery(parser.text()); + break; + case NUMBER_OF_RESULTS_FIELD: + entry.setNumberOfResults(parser.longValue()); + break; + case AlertActionState.FIELD_NAME: + entry.setEntryState(AlertActionState.fromString(parser.text())); + break; + default: + throw new ElasticsearchIllegalArgumentException("Unexpected field [" + currentFieldName + "]"); + } + } else { + + } + } + } catch (IOException e) { + throw new ElasticsearchException("Error during parsing alert action", e); } - - List indices = new ArrayList<>(); - if (fields.get(INDICES_FIELD) != null && fields.get(INDICES_FIELD) instanceof List){ - indices = (List)fields.get(INDICES_FIELD); - } else { - logger.debug("Indices : " + fields.get(INDICES_FIELD) + " class " + - (fields.get(INDICES_FIELD) != null ? fields.get(INDICES_FIELD).getClass() : null )); - } - - String stateString = fields.get(AlertActionState.FIELD_NAME).toString(); - AlertActionState state = AlertActionState.fromString(stateString); - - return new AlertActionEntry(historyId, version, alertName, triggered, fireTime, scheduledFireTime, trigger, queryRan, - numberOfResults, actions, indices, state); + return entry; } @@ -309,7 +341,7 @@ public class AlertActionManager { getRequest.id(entryId); GetResponse getResponse = client.get(getRequest).actionGet(); if (getResponse.isExists()) { - return parseHistory(entryId, getResponse.getSourceAsMap(), getResponse.getVersion()); + return parseHistory(entryId, getResponse.getSourceAsBytesRef(), getResponse.getVersion()); } else { throw new ElasticsearchException("Unable to find [" + entryId + "] in the [" + ALERT_HISTORY_INDEX + "]" ); } diff --git a/src/main/java/org/elasticsearch/alerts/actions/AlertActionRegistry.java b/src/main/java/org/elasticsearch/alerts/actions/AlertActionRegistry.java index 6d3300ad6fe..9a1055e2f4d 100644 --- a/src/main/java/org/elasticsearch/alerts/actions/AlertActionRegistry.java +++ b/src/main/java/org/elasticsearch/alerts/actions/AlertActionRegistry.java @@ -7,16 +7,16 @@ package org.elasticsearch.alerts.actions; import org.elasticsearch.ElasticsearchIllegalArgumentException; import org.elasticsearch.alerts.Alert; -import org.elasticsearch.alerts.AlertManager; import org.elasticsearch.client.Client; import org.elasticsearch.common.collect.ImmutableOpenMap; 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 java.io.IOException; import java.util.ArrayList; import java.util.List; -import java.util.Map; public class AlertActionRegistry extends AbstractComponent { @@ -37,16 +37,23 @@ public class AlertActionRegistry extends AbstractComponent { .build(); } - public List parseActionsFromMap(Map actionMap) { - ImmutableOpenMap actionImplemented = this.actionImplemented; + public List instantiateAlertActions(XContentParser parser) throws IOException { List actions = new ArrayList<>(); - for (Map.Entry actionEntry : actionMap.entrySet()) { - AlertActionFactory factory = actionImplemented.get(actionEntry.getKey()); - if (factory != null) { - actions.add(factory.createAction(actionEntry.getValue())); - } else { - throw new ElasticsearchIllegalArgumentException("No action exists with the name [" + actionEntry.getKey() + "]"); + ImmutableOpenMap actionImplemented = this.actionImplemented; + String actionFactoryName = null; + XContentParser.Token token; + while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { + if (token == XContentParser.Token.FIELD_NAME) { + actionFactoryName = parser.currentName(); + } else if (token == XContentParser.Token.START_OBJECT) { + AlertActionFactory factory = actionImplemented.get(actionFactoryName); + if (factory != null) { + actions.add(factory.createAction(parser)); + } else { + throw new ElasticsearchIllegalArgumentException("No action exists with the name [" + actionFactoryName + "]"); + } } + } return actions; } diff --git a/src/main/java/org/elasticsearch/alerts/actions/EmailAlertAction.java b/src/main/java/org/elasticsearch/alerts/actions/EmailAlertAction.java index a952ffd624f..65505120944 100644 --- a/src/main/java/org/elasticsearch/alerts/actions/EmailAlertAction.java +++ b/src/main/java/org/elasticsearch/alerts/actions/EmailAlertAction.java @@ -9,25 +9,27 @@ import org.elasticsearch.ElasticsearchException; import org.elasticsearch.alerts.Alert; import org.elasticsearch.common.xcontent.XContentBuilder; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Properties; import javax.mail.*; import javax.mail.internet.AddressException; import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeMessage; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Properties; public class EmailAlertAction implements AlertAction { - List
emailAddresses = new ArrayList<>(); - String displayField = null; + private String displayField = null; + private List
emailAddresses = new ArrayList<>(); + + // TODO: Move to factory and make configurable + int port = 587; + String server = "smtp.gmail.com"; String from = "esalertingtest@gmail.com"; String passwd = "elasticsearchforthewin"; - String server = "smtp.gmail.com"; - int port = 587; - public EmailAlertAction(String ... addresses){ + public EmailAlertAction(String displayField, String ... addresses){ for (String address : addresses) { addEmailAddress(address); } diff --git a/src/main/java/org/elasticsearch/alerts/actions/EmailAlertActionFactory.java b/src/main/java/org/elasticsearch/alerts/actions/EmailAlertActionFactory.java index 57ef4b68a32..ddb431ba947 100644 --- a/src/main/java/org/elasticsearch/alerts/actions/EmailAlertActionFactory.java +++ b/src/main/java/org/elasticsearch/alerts/actions/EmailAlertActionFactory.java @@ -5,37 +5,47 @@ */ package org.elasticsearch.alerts.actions; -import org.elasticsearch.ElasticsearchException; import org.elasticsearch.ElasticsearchIllegalArgumentException; +import org.elasticsearch.common.xcontent.XContentParser; +import java.io.IOException; +import java.util.ArrayList; import java.util.List; -import java.util.Map; public class EmailAlertActionFactory implements AlertActionFactory { @Override - public AlertAction createAction(Object parameters) { - EmailAlertAction action = new EmailAlertAction(); - if (parameters instanceof List){ - for (String emailAddress : (List)parameters) { - action.addEmailAddress(emailAddress); + public AlertAction createAction(XContentParser parser) throws IOException { + String display = null; + List addresses = new ArrayList<>(); + + String currentFieldName = null; + XContentParser.Token token; + while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { + if (token == XContentParser.Token.FIELD_NAME) { + currentFieldName = parser.currentName(); + } else if (token.isValue()) { + switch (currentFieldName) { + case "display": + display = parser.text(); + break; + default: + throw new ElasticsearchIllegalArgumentException("Unexpected field [" + currentFieldName + "]"); + } + } else if (token == XContentParser.Token.START_ARRAY) { + switch (currentFieldName) { + case "addresses": + while (parser.nextToken() != XContentParser.Token.END_ARRAY) { + addresses.add(parser.text()); + } + break; + default: + throw new ElasticsearchIllegalArgumentException("Unexpected field [" + currentFieldName + "]"); + } + } else { + throw new ElasticsearchIllegalArgumentException("Unexpected token [" + token + "]"); } - } else if (parameters instanceof Map) { - Map paramMap = (Map)parameters; - Object addresses = paramMap.get("addresses"); - if (addresses == null){ - throw new ElasticsearchException("Unable to parse email addresses from : " + parameters); - } - for (String emailAddress : (List)addresses) { - action.addEmailAddress(emailAddress); - } - Object displayField = paramMap.get("display"); - if (displayField != null){ - action.displayField(displayField.toString()); - } - } else { - throw new ElasticsearchIllegalArgumentException("Unable to parse [" + parameters + "] as an EmailAlertAction"); } - return action; + return new EmailAlertAction(display, addresses.toArray(new String[addresses.size()])); } } diff --git a/src/main/java/org/elasticsearch/alerts/actions/IndexAlertActionFactory.java b/src/main/java/org/elasticsearch/alerts/actions/IndexAlertActionFactory.java index f4df489edca..d9edcb844b3 100644 --- a/src/main/java/org/elasticsearch/alerts/actions/IndexAlertActionFactory.java +++ b/src/main/java/org/elasticsearch/alerts/actions/IndexAlertActionFactory.java @@ -7,42 +7,46 @@ package org.elasticsearch.alerts.actions; import org.elasticsearch.ElasticsearchIllegalArgumentException; import org.elasticsearch.client.Client; +import org.elasticsearch.common.xcontent.XContentParser; -import java.util.Locale; -import java.util.Map; +import java.io.IOException; /** * Created by brian on 8/17/14. */ public class IndexAlertActionFactory implements AlertActionFactory { - Client client; + private final Client client; public IndexAlertActionFactory(Client client){ this.client = client; } @Override - public AlertAction createAction(Object parameters) { - try { - if (parameters instanceof Map) { - Map paramMap = (Map) parameters; - String index = paramMap.get("index").toString(); - if (!index.toLowerCase(Locale.ROOT).equals(index)) { - throw new ElasticsearchIllegalArgumentException("Index names must be all lowercase"); - } + public AlertAction createAction(XContentParser parser) throws IOException { + String index = null; + String type = null; - String type = paramMap.get("type").toString(); - if (!type.toLowerCase(Locale.ROOT).equals(type)) { - throw new ElasticsearchIllegalArgumentException("Type names must be all lowercase"); + String currentFieldName = null; + XContentParser.Token token; + while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { + if (token == XContentParser.Token.FIELD_NAME) { + currentFieldName = parser.currentName(); + } else if (token.isValue()) { + switch (currentFieldName) { + case "index": + index = parser.text(); + break; + case "type": + type = parser.text(); + break; + default: + throw new ElasticsearchIllegalArgumentException("Unexpected field [" + currentFieldName + "]"); } - - return new IndexAlertAction(index, type, client); } else { - throw new ElasticsearchIllegalArgumentException("Unable to parse [" + parameters + "] as an IndexAlertAction"); + throw new ElasticsearchIllegalArgumentException("Unexpected token [" + token + "]"); } - } catch (Throwable t){ - throw new ElasticsearchIllegalArgumentException("Unable to parse [" + parameters + "] as an IndexAlertAction", t); } + return new IndexAlertAction(index, type, client); } } diff --git a/src/main/java/org/elasticsearch/alerts/triggers/TriggerManager.java b/src/main/java/org/elasticsearch/alerts/triggers/TriggerManager.java index c13955103e6..420427b902f 100644 --- a/src/main/java/org/elasticsearch/alerts/triggers/TriggerManager.java +++ b/src/main/java/org/elasticsearch/alerts/triggers/TriggerManager.java @@ -13,14 +13,11 @@ import org.elasticsearch.alerts.AlertManager; 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.XContentFactory; -import org.elasticsearch.common.xcontent.XContentHelper; +import org.elasticsearch.common.xcontent.*; import org.elasticsearch.script.ExecutableScript; import org.elasticsearch.script.ScriptService; -import java.util.Locale; +import java.io.IOException; import java.util.Map; @@ -32,34 +29,52 @@ public class TriggerManager extends AbstractComponent { private final AlertManager alertManager; private final ScriptService scriptService; - public static AlertTrigger parseTriggerFromMap(Map triggerMap) { - for (Map.Entry entry : triggerMap.entrySet()){ - AlertTrigger.TriggerType type = AlertTrigger.TriggerType.fromString(entry.getKey()); - if (type == AlertTrigger.TriggerType.SCRIPT) { - ScriptedAlertTrigger scriptedTrigger = parseScriptedTrigger(entry.getValue()); - return new AlertTrigger(scriptedTrigger); - } else { - AlertTrigger.SimpleTrigger simpleTrigger = AlertTrigger.SimpleTrigger.fromString(entry.getValue().toString().substring(0, 1)); - int value = Integer.valueOf(entry.getValue().toString().substring(1)); - return new AlertTrigger(simpleTrigger, type, value); - } - } - throw new ElasticsearchIllegalArgumentException(); - } + public static AlertTrigger parseTrigger(XContentParser parser) throws IOException { + AlertTrigger trigger = null; - private static ScriptedAlertTrigger parseScriptedTrigger(Object value) { - if (value instanceof Map) { - Map valueMap = (Map)value; - try { - return new ScriptedAlertTrigger(valueMap.get("script").toString(), - ScriptService.ScriptType.valueOf(valueMap.get("script_type").toString().toUpperCase(Locale.ROOT)), ///TODO : Fix ScriptType to parse strings properly, currently only accepts uppercase versions of the enum names - valueMap.get("script_lang").toString()); - } catch (Exception e){ - throw new ElasticsearchIllegalArgumentException("Unable to parse " + value + " as a ScriptedAlertTrigger", e); + String currentFieldName = null; + XContentParser.Token token; + while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { + if (token == XContentParser.Token.FIELD_NAME) { + currentFieldName = parser.currentName(); + } else if (token == XContentParser.Token.START_OBJECT) { + switch (currentFieldName) { + case "script": + String script = null; + ScriptService.ScriptType scriptType = null; + String scriptLang = null; + while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { + if (token == XContentParser.Token.FIELD_NAME) { + currentFieldName = parser.currentName(); + } else if (token.isValue()) { + switch (currentFieldName) { + case "script" : + script = parser.text(); + break; + case "script_type" : + scriptType = ScriptService.ScriptType.valueOf(parser.text()); + break; + case "script_lang" : + scriptLang = parser.text(); + break; + default: + throw new ElasticsearchIllegalArgumentException("Unexpected field [" + currentFieldName + "]"); + } + } + } + trigger = new AlertTrigger(new ScriptedAlertTrigger(script, scriptType, scriptLang)); + break; + default: + break; + } + } else if (token.isValue()) { + String expression = parser.text(); + AlertTrigger.SimpleTrigger simpleTrigger = AlertTrigger.SimpleTrigger.fromString(expression.substring(0, 1)); + int value = Integer.valueOf(expression.substring(1)); + trigger = new AlertTrigger(simpleTrigger, AlertTrigger.TriggerType.NUMBER_OF_EVENTS, value); } - } else { - throw new ElasticsearchIllegalArgumentException("Unable to parse " + value + " as a ScriptedAlertTrigger, not a Map"); } + return trigger; } @Inject diff --git a/src/test/java/org/elasticsearch/alerts/BasicAlertingTest.java b/src/test/java/org/elasticsearch/alerts/BasicAlertingTest.java index 846dde83814..ea281f309f2 100644 --- a/src/test/java/org/elasticsearch/alerts/BasicAlertingTest.java +++ b/src/test/java/org/elasticsearch/alerts/BasicAlertingTest.java @@ -15,6 +15,7 @@ import org.elasticsearch.common.settings.ImmutableSettings; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.script.ScriptService; import org.elasticsearch.test.ElasticsearchIntegrationTest; import org.junit.Test; @@ -103,7 +104,8 @@ public class BasicAlertingTest extends ElasticsearchIntegrationTest { AlertActionRegistry alertActionRegistry = internalCluster().getInstance(AlertActionRegistry.class, internalCluster().getMasterName()); alertActionRegistry.registerAction("test", new AlertActionFactory() { @Override - public AlertAction createAction(Object parameters) { + public AlertAction createAction(XContentParser parser) throws IOException { + parser.nextToken(); return alertAction; } }); diff --git a/src/test/java/org/elasticsearch/alerts/actions/AlertActionsTest.java b/src/test/java/org/elasticsearch/alerts/actions/AlertActionsTest.java index 04e0ac235ef..f5d939464bb 100644 --- a/src/test/java/org/elasticsearch/alerts/actions/AlertActionsTest.java +++ b/src/test/java/org/elasticsearch/alerts/actions/AlertActionsTest.java @@ -5,9 +5,12 @@ */ package org.elasticsearch.alerts.actions; -import org.elasticsearch.alerts.BasicAlertingTest; import org.elasticsearch.alerts.triggers.AlertTrigger; +import org.elasticsearch.common.joda.FormatDateTimeFormatter; import org.elasticsearch.common.joda.time.DateTime; +import org.elasticsearch.common.joda.time.DateTimeZone; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.index.mapper.core.DateFieldMapper; import org.elasticsearch.test.ElasticsearchIntegrationTest; import org.junit.Test; @@ -16,15 +19,19 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; + /** */ @ElasticsearchIntegrationTest.ClusterScope(scope = ElasticsearchIntegrationTest.Scope.SUITE, numClientNodes = 0, transportClientRatio = 0, numDataNodes = 1) public class AlertActionsTest extends ElasticsearchIntegrationTest { + private static final FormatDateTimeFormatter formatter = DateFieldMapper.Defaults.DATE_TIME_FORMATTER; + @Test - public void testAlertActionParser(){ - DateTime fireTime = new DateTime(); - DateTime scheduledFireTime = new DateTime(); + public void testAlertActionParser() throws Exception { + DateTime fireTime = new DateTime(DateTimeZone.UTC); + DateTime scheduledFireTime = new DateTime(DateTimeZone.UTC); Map triggerMap = new HashMap<>(); triggerMap.put("numberOfEvents", ">1"); Map actionMap = new HashMap<>(); @@ -34,18 +41,20 @@ public class AlertActionsTest extends ElasticsearchIntegrationTest { emailParamMap.put("addresses", addresses); actionMap.put("email", emailParamMap); - Map fieldMap = new HashMap<>(); - fieldMap.put(AlertActionManager.ALERT_NAME_FIELD, "testName"); - fieldMap.put(AlertActionManager.TRIGGERED_FIELD, true); - fieldMap.put(AlertActionManager.FIRE_TIME_FIELD, fireTime.toDateTimeISO().toString()); - fieldMap.put(AlertActionManager.SCHEDULED_FIRE_TIME_FIELD, scheduledFireTime.toDateTimeISO().toString()); - fieldMap.put(AlertActionManager.TRIGGER_FIELD, triggerMap); - fieldMap.put(AlertActionManager.QUERY_RAN_FIELD, "foobar"); - fieldMap.put(AlertActionManager.NUMBER_OF_RESULTS_FIELD,10); - fieldMap.put(AlertActionManager.ACTIONS_FIELD, actionMap); - fieldMap.put(AlertActionState.FIELD_NAME, AlertActionState.ACTION_NEEDED.toString()); + XContentBuilder builder = jsonBuilder(); + builder.startObject(); + builder.field(AlertActionManager.ALERT_NAME_FIELD, "testName"); + builder.field(AlertActionManager.TRIGGERED_FIELD, true); + builder.field(AlertActionManager.FIRE_TIME_FIELD, formatter.printer().print(fireTime)); + builder.field(AlertActionManager.SCHEDULED_FIRE_TIME_FIELD, formatter.printer().print(scheduledFireTime)); + builder.field(AlertActionManager.TRIGGER_FIELD, triggerMap); + builder.field(AlertActionManager.QUERY_RAN_FIELD, "foobar"); + builder.field(AlertActionManager.NUMBER_OF_RESULTS_FIELD, 10); + builder.field(AlertActionManager.ACTIONS_FIELD, actionMap); + builder.field(AlertActionState.FIELD_NAME, AlertActionState.ACTION_NEEDED.toString()); + builder.endObject(); AlertActionRegistry alertActionRegistry = internalCluster().getInstance(AlertActionRegistry.class, internalCluster().getMasterName()); - AlertActionEntry actionEntry = AlertActionManager.parseHistory("foobar", fieldMap, 0, alertActionRegistry, logger); + AlertActionEntry actionEntry = AlertActionManager.parseHistory("foobar", builder.bytes(), 0, alertActionRegistry, logger); assertEquals(actionEntry.getVersion(), 0); assertEquals(actionEntry.getAlertName(), "testName");