/*
* Copyright 2012 LinkedIn Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package azkaban.trigger;
import static java.util.Objects.requireNonNull;
import azkaban.utils.JSONUtils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.log4j.Logger;
import org.joda.time.DateTime;
public class Trigger {
private static final Logger logger = Logger.getLogger(Trigger.class);
private static ActionTypeLoader actionTypeLoader;
private final long submitTime;
private final String submitUser;
private final String source;
private final List<TriggerAction> actions;
private final List<TriggerAction> expireActions;
private Condition expireCondition;
private Condition triggerCondition;
private int triggerId = -1;
private long lastModifyTime;
private TriggerStatus status = TriggerStatus.READY;
private Map<String, Object> info = new HashMap<>();
private Map<String, Object> context = new HashMap<>();
private boolean resetOnTrigger = true;
private boolean resetOnExpire = true;
private long nextCheckTime = -1;
private Trigger() throws TriggerManagerException {
throw new TriggerManagerException("Triggers should always be specified");
}
private Trigger(final int triggerId, final long lastModifyTime, final long submitTime,
final String submitUser, final String source, final Condition triggerCondition,
final Condition expireCondition, final List<TriggerAction> actions,
final List<TriggerAction> expireActions, final Map<String, Object> info,
final Map<String, Object> context) {
requireNonNull(submitUser);
requireNonNull(source);
requireNonNull(triggerCondition);
requireNonNull(expireActions);
requireNonNull(info);
requireNonNull(context);
this.lastModifyTime = lastModifyTime;
this.submitTime = submitTime;
this.submitUser = submitUser;
this.source = source;
this.triggerCondition = triggerCondition;
this.expireCondition = expireCondition;
this.actions = actions;
this.triggerId = triggerId;
this.expireActions = expireActions;
this.info = info;
this.context = context;
}
public static ActionTypeLoader getActionTypeLoader() {
return actionTypeLoader;
}
public static synchronized void setActionTypeLoader(final ActionTypeLoader loader) {
Trigger.actionTypeLoader = loader;
}
public static Trigger fromJson(final Object obj) throws Exception {
if (actionTypeLoader == null) {
throw new Exception("Trigger Action Type loader not initialized.");
}
final Map<String, Object> jsonObj = (HashMap<String, Object>) obj;
Trigger trigger = null;
try {
logger.info("Decoding for " + JSONUtils.toJSON(obj));
final Condition triggerCond = Condition.fromJson(jsonObj.get("triggerCondition"));
final Condition expireCond = Condition.fromJson(jsonObj.get("expireCondition"));
final List<TriggerAction> actions = new ArrayList<>();
final List<Object> actionsJson = (List<Object>) jsonObj.get("actions");
for (final Object actObj : actionsJson) {
final Map<String, Object> oneActionJson = (HashMap<String, Object>) actObj;
final String type = (String) oneActionJson.get("type");
final TriggerAction act =
actionTypeLoader.createActionFromJson(type,
oneActionJson.get("actionJson"));
actions.add(act);
}
final List<TriggerAction> expireActions = new ArrayList<>();
final List<Object> expireActionsJson =
(List<Object>) jsonObj.get("expireActions");
for (final Object expireActObj : expireActionsJson) {
final Map<String, Object> oneExpireActionJson =
(HashMap<String, Object>) expireActObj;
final String type = (String) oneExpireActionJson.get("type");
final TriggerAction expireAct =
actionTypeLoader.createActionFromJson(type,
oneExpireActionJson.get("actionJson"));
expireActions.add(expireAct);
}
final boolean resetOnTrigger =
Boolean.valueOf((String) jsonObj.get("resetOnTrigger"));
final boolean resetOnExpire =
Boolean.valueOf((String) jsonObj.get("resetOnExpire"));
final String submitUser = (String) jsonObj.get("submitUser");
final String source = (String) jsonObj.get("source");
final long submitTime = Long.valueOf((String) jsonObj.get("submitTime"));
final long lastModifyTime =
Long.valueOf((String) jsonObj.get("lastModifyTime"));
final int triggerId = Integer.valueOf((String) jsonObj.get("triggerId"));
final TriggerStatus status =
TriggerStatus.valueOf((String) jsonObj.get("status"));
final Map<String, Object> info = (Map<String, Object>) jsonObj.get("info");
Map<String, Object> context =
(Map<String, Object>) jsonObj.get("context");
if (context == null) {
context = new HashMap<>();
}
for (final ConditionChecker checker : triggerCond.getCheckers().values()) {
checker.setContext(context);
}
for (final ConditionChecker checker : expireCond.getCheckers().values()) {
checker.setContext(context);
}
for (final TriggerAction action : actions) {
action.setContext(context);
}
for (final TriggerAction action : expireActions) {
action.setContext(context);
}
trigger = new Trigger.TriggerBuilder("azkaban",
source,
triggerCond,
expireCond,
actions)
.setId(triggerId)
.setLastModifyTime(lastModifyTime)
.setSubmitTime(submitTime)
.setExpireActions(expireActions)
.setInfo(info)
.setContext(context)
.build();
trigger.setResetOnExpire(resetOnExpire);
trigger.setResetOnTrigger(resetOnTrigger);
trigger.setStatus(status);
} catch (final Exception e) {
e.printStackTrace();
logger.error("Failed to decode the trigger.", e);
throw new Exception("Failed to decode the trigger.", e);
}
return trigger;
}
public void updateNextCheckTime() {
this.nextCheckTime = Math.min(this.triggerCondition.getNextCheckTime(),
this.expireCondition.getNextCheckTime());
}
public long getNextCheckTime() {
return this.nextCheckTime;
}
public void setNextCheckTime(final long nct) {
this.nextCheckTime = nct;
}
public long getSubmitTime() {
return this.submitTime;
}
public String getSubmitUser() {
return this.submitUser;
}
public TriggerStatus getStatus() {
return this.status;
}
public void setStatus(final TriggerStatus status) {
this.status = status;
}
public Condition getTriggerCondition() {
return this.triggerCondition;
}
public void setTriggerCondition(final Condition triggerCondition) {
this.triggerCondition = triggerCondition;
}
public Condition getExpireCondition() {
return this.expireCondition;
}
public void setExpireCondition(final Condition expireCondition) {
this.expireCondition = expireCondition;
}
public List<TriggerAction> getActions() {
return this.actions;
}
public List<TriggerAction> getExpireActions() {
return this.expireActions;
}
public Map<String, Object> getInfo() {
return this.info;
}
public void setInfo(final Map<String, Object> info) {
this.info = info;
}
public Map<String, Object> getContext() {
return this.context;
}
public void setContext(final Map<String, Object> context) {
this.context = context;
}
public boolean isResetOnTrigger() {
return this.resetOnTrigger;
}
public void setResetOnTrigger(final boolean resetOnTrigger) {
this.resetOnTrigger = resetOnTrigger;
}
public boolean isResetOnExpire() {
return this.resetOnExpire;
}
public void setResetOnExpire(final boolean resetOnExpire) {
this.resetOnExpire = resetOnExpire;
}
public long getLastModifyTime() {
return this.lastModifyTime;
}
public void setLastModifyTime(final long lastModifyTime) {
this.lastModifyTime = lastModifyTime;
}
public int getTriggerId() {
return this.triggerId;
}
public void setTriggerId(final int id) {
this.triggerId = id;
}
public boolean triggerConditionMet() {
return this.triggerCondition.isMet();
}
public boolean expireConditionMet() {
return this.expireCondition.isMet();
}
public void resetTriggerConditions() {
this.triggerCondition.resetCheckers();
updateNextCheckTime();
}
public void resetExpireCondition() {
this.expireCondition.resetCheckers();
updateNextCheckTime();
}
public List<TriggerAction> getTriggerActions() {
return this.actions;
}
public Map<String, Object> toJson() {
final Map<String, Object> jsonObj = new HashMap<>();
jsonObj.put("triggerCondition", this.triggerCondition.toJson());
jsonObj.put("expireCondition", this.expireCondition.toJson());
final List<Object> actionsJson = new ArrayList<>();
for (final TriggerAction action : this.actions) {
final Map<String, Object> oneActionJson = new HashMap<>();
oneActionJson.put("type", action.getType());
oneActionJson.put("actionJson", action.toJson());
actionsJson.add(oneActionJson);
}
jsonObj.put("actions", actionsJson);
final List<Object> expireActionsJson = new ArrayList<>();
for (final TriggerAction expireAction : this.expireActions) {
final Map<String, Object> oneExpireActionJson = new HashMap<>();
oneExpireActionJson.put("type", expireAction.getType());
oneExpireActionJson.put("actionJson", expireAction.toJson());
expireActionsJson.add(oneExpireActionJson);
}
jsonObj.put("expireActions", expireActionsJson);
jsonObj.put("resetOnTrigger", String.valueOf(this.resetOnTrigger));
jsonObj.put("resetOnExpire", String.valueOf(this.resetOnExpire));
jsonObj.put("submitUser", this.submitUser);
jsonObj.put("source", this.source);
jsonObj.put("submitTime", String.valueOf(this.submitTime));
jsonObj.put("lastModifyTime", String.valueOf(this.lastModifyTime));
jsonObj.put("triggerId", String.valueOf(this.triggerId));
jsonObj.put("status", this.status.toString());
jsonObj.put("info", this.info);
jsonObj.put("context", this.context);
return jsonObj;
}
public String getSource() {
return this.source;
}
public String getDescription() {
final StringBuffer actionsString = new StringBuffer();
for (final TriggerAction act : this.actions) {
actionsString.append(", ");
actionsString.append(act.getDescription());
}
return "Trigger from " + getSource() + " with trigger condition of "
+ this.triggerCondition.getExpression() + " and expire condition of "
+ this.expireCondition.getExpression() + actionsString;
}
public void stopCheckers() {
for (final ConditionChecker checker : this.triggerCondition.getCheckers().values()) {
checker.stopChecker();
}
for (final ConditionChecker checker : this.expireCondition.getCheckers().values()) {
checker.stopChecker();
}
}
@Override
public String toString() {
return "Trigger Id: " + getTriggerId() + ", Description: " + getDescription();
}
public static class TriggerBuilder {
private final String submitUser;
private final String source;
private final TriggerStatus status = TriggerStatus.READY;
private final Condition triggerCondition;
private final List<TriggerAction> actions;
private final Condition expireCondition;
private int triggerId = -1;
private long lastModifyTime;
private long submitTime;
private List<TriggerAction> expireActions = new ArrayList<>();
private Map<String, Object> info = new HashMap<>();
private Map<String, Object> context = new HashMap<>();
public TriggerBuilder(final String submitUser,
final String source,
final Condition triggerCondition,
final Condition expireCondition,
final List<TriggerAction> actions) {
this.submitUser = submitUser;
this.source = source;
this.triggerCondition = triggerCondition;
this.actions = actions;
this.expireCondition = expireCondition;
final long now = DateTime.now().getMillis();
this.submitTime = now;
this.lastModifyTime = now;
}
public TriggerBuilder setId(final int id) {
this.triggerId = id;
return this;
}
public TriggerBuilder setSubmitTime(final long time) {
this.submitTime = time;
return this;
}
public TriggerBuilder setLastModifyTime(final long time) {
this.lastModifyTime = time;
return this;
}
public TriggerBuilder setExpireActions(final List<TriggerAction> actions) {
this.expireActions = actions;
return this;
}
public TriggerBuilder setInfo(final Map<String, Object> info) {
this.info = info;
return this;
}
public TriggerBuilder setContext(final Map<String, Object> context) {
this.context = context;
return this;
}
public Trigger build() {
return new Trigger(this.triggerId,
this.lastModifyTime,
this.submitTime,
this.submitUser,
this.source,
this.triggerCondition,
this.expireCondition,
this.actions,
this.expireActions,
this.info,
this.context);
}
}
}