/*
* 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 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;
import azkaban.utils.JSONUtils;
import static java.util.Objects.*;
public class Trigger {
private static Logger logger = Logger.getLogger(Trigger.class);
private int triggerId = -1;
private long lastModifyTime;
private final long submitTime;
private final String submitUser;
private final String source;
private TriggerStatus status = TriggerStatus.READY;
private final Condition triggerCondition;
private final Condition expireCondition;
private final List<TriggerAction> actions;
private final List<TriggerAction> expireActions;
private Map<String, Object> info = new HashMap<String, Object>();
private Map<String, Object> context = new HashMap<String, Object>();
private static ActionTypeLoader actionTypeLoader;
private boolean resetOnTrigger = true;
private boolean resetOnExpire = true;
private long nextCheckTime = -1;
@SuppressWarnings("unused")
private Trigger() throws TriggerManagerException {
throw new TriggerManagerException("Triggers should always be specified");
}
public void updateNextCheckTime() {
this.nextCheckTime =
Math.min(triggerCondition.getNextCheckTime(),
expireCondition.getNextCheckTime());
}
public long getNextCheckTime() {
return nextCheckTime;
}
public void setNextCheckTime(long nct) {
this.nextCheckTime = nct;
}
public long getSubmitTime() {
return submitTime;
}
public String getSubmitUser() {
return submitUser;
}
public TriggerStatus getStatus() {
return status;
}
public void setStatus(TriggerStatus status) {
this.status = status;
}
public Condition getTriggerCondition() {
return triggerCondition;
}
public Condition getExpireCondition() {
return expireCondition;
}
public List<TriggerAction> getActions() {
return actions;
}
public List<TriggerAction> getExpireActions() {
return expireActions;
}
public Map<String, Object> getInfo() {
return info;
}
public void setInfo(Map<String, Object> info) {
this.info = info;
}
public Map<String, Object> getContext() {
return context;
}
public void setContext(Map<String, Object> context) {
this.context = context;
}
public static class TriggerBuilder {
private int triggerId = -1;
private long lastModifyTime;
private long submitTime;
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 List<TriggerAction> expireActions = new ArrayList<>();
private Map<String, Object> info = new HashMap<String, Object>();
private Map<String, Object> context = new HashMap<String, Object>();
public TriggerBuilder(String submitUser,
String source,
Condition triggerCondition,
Condition expireCondition,
List<TriggerAction> actions) {
this.submitUser = submitUser;
this.source = source;
this.triggerCondition = triggerCondition;
this.actions = actions;
this.expireCondition = expireCondition;
long now = DateTime.now().getMillis();
this.submitTime = now;
this.lastModifyTime = now;
}
public TriggerBuilder setId(int id) {
this.triggerId = id;
return this;
}
public TriggerBuilder setSubmitTime(long time) {
this.submitTime = time;
return this;
}
public TriggerBuilder setLastModifyTime(long time) {
this.lastModifyTime = time;
return this;
}
public TriggerBuilder setExpireActions(List<TriggerAction> actions) {
this.expireActions = actions;
return this;
}
public TriggerBuilder setInfo(Map<String, Object> info) {
this.info = info;
return this;
}
public TriggerBuilder setContext(Map<String, Object> context) {
this.context = context;
return this;
}
public Trigger build() {
return new Trigger(triggerId,
lastModifyTime,
submitTime,
submitUser,
source,
triggerCondition,
expireCondition,
actions,
expireActions,
info,
context);
}
}
private Trigger(int triggerId, long lastModifyTime, long submitTime,
String submitUser, String source, Condition triggerCondition,
Condition expireCondition, List<TriggerAction> actions,
List<TriggerAction> expireActions, Map<String, Object> info,
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 synchronized void setActionTypeLoader(ActionTypeLoader loader) {
Trigger.actionTypeLoader = loader;
}
public static ActionTypeLoader getActionTypeLoader() {
return actionTypeLoader;
}
public boolean isResetOnTrigger() {
return resetOnTrigger;
}
public void setResetOnTrigger(boolean resetOnTrigger) {
this.resetOnTrigger = resetOnTrigger;
}
public boolean isResetOnExpire() {
return resetOnExpire;
}
public void setResetOnExpire(boolean resetOnExpire) {
this.resetOnExpire = resetOnExpire;
}
public long getLastModifyTime() {
return lastModifyTime;
}
public void setLastModifyTime(long lastModifyTime) {
this.lastModifyTime = lastModifyTime;
}
public void setTriggerId(int id) {
this.triggerId = id;
}
public int getTriggerId() {
return triggerId;
}
public boolean triggerConditionMet() {
return triggerCondition.isMet();
}
public boolean expireConditionMet() {
return expireCondition.isMet();
}
public void resetTriggerConditions() {
triggerCondition.resetCheckers();
updateNextCheckTime();
}
public void resetExpireCondition() {
expireCondition.resetCheckers();
updateNextCheckTime();
}
public List<TriggerAction> getTriggerActions() {
return actions;
}
public Map<String, Object> toJson() {
Map<String, Object> jsonObj = new HashMap<String, Object>();
jsonObj.put("triggerCondition", triggerCondition.toJson());
jsonObj.put("expireCondition", expireCondition.toJson());
List<Object> actionsJson = new ArrayList<Object>();
for (TriggerAction action : actions) {
Map<String, Object> oneActionJson = new HashMap<String, Object>();
oneActionJson.put("type", action.getType());
oneActionJson.put("actionJson", action.toJson());
actionsJson.add(oneActionJson);
}
jsonObj.put("actions", actionsJson);
List<Object> expireActionsJson = new ArrayList<Object>();
for (TriggerAction expireAction : expireActions) {
Map<String, Object> oneExpireActionJson = new HashMap<String, Object>();
oneExpireActionJson.put("type", expireAction.getType());
oneExpireActionJson.put("actionJson", expireAction.toJson());
expireActionsJson.add(oneExpireActionJson);
}
jsonObj.put("expireActions", expireActionsJson);
jsonObj.put("resetOnTrigger", String.valueOf(resetOnTrigger));
jsonObj.put("resetOnExpire", String.valueOf(resetOnExpire));
jsonObj.put("submitUser", submitUser);
jsonObj.put("source", source);
jsonObj.put("submitTime", String.valueOf(submitTime));
jsonObj.put("lastModifyTime", String.valueOf(lastModifyTime));
jsonObj.put("triggerId", String.valueOf(triggerId));
jsonObj.put("status", status.toString());
jsonObj.put("info", info);
jsonObj.put("context", context);
return jsonObj;
}
public String getSource() {
return source;
}
@SuppressWarnings("unchecked")
public static Trigger fromJson(Object obj) throws Exception {
if (actionTypeLoader == null) {
throw new Exception("Trigger Action Type loader not initialized.");
}
Map<String, Object> jsonObj = (HashMap<String, Object>) obj;
Trigger trigger = null;
try {
logger.info("Decoding for " + JSONUtils.toJSON(obj));
Condition triggerCond =
Condition.fromJson(jsonObj.get("triggerCondition"));
Condition expireCond = Condition.fromJson(jsonObj.get("expireCondition"));
List<TriggerAction> actions = new ArrayList<TriggerAction>();
List<Object> actionsJson = (List<Object>) jsonObj.get("actions");
for (Object actObj : actionsJson) {
Map<String, Object> oneActionJson = (HashMap<String, Object>) actObj;
String type = (String) oneActionJson.get("type");
TriggerAction act =
actionTypeLoader.createActionFromJson(type,
oneActionJson.get("actionJson"));
actions.add(act);
}
List<TriggerAction> expireActions = new ArrayList<TriggerAction>();
List<Object> expireActionsJson =
(List<Object>) jsonObj.get("expireActions");
for (Object expireActObj : expireActionsJson) {
Map<String, Object> oneExpireActionJson =
(HashMap<String, Object>) expireActObj;
String type = (String) oneExpireActionJson.get("type");
TriggerAction expireAct =
actionTypeLoader.createActionFromJson(type,
oneExpireActionJson.get("actionJson"));
expireActions.add(expireAct);
}
boolean resetOnTrigger =
Boolean.valueOf((String) jsonObj.get("resetOnTrigger"));
boolean resetOnExpire =
Boolean.valueOf((String) jsonObj.get("resetOnExpire"));
String submitUser = (String) jsonObj.get("submitUser");
String source = (String) jsonObj.get("source");
long submitTime = Long.valueOf((String) jsonObj.get("submitTime"));
long lastModifyTime =
Long.valueOf((String) jsonObj.get("lastModifyTime"));
int triggerId = Integer.valueOf((String) jsonObj.get("triggerId"));
TriggerStatus status =
TriggerStatus.valueOf((String) jsonObj.get("status"));
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<String, Object>();
}
for (ConditionChecker checker : triggerCond.getCheckers().values()) {
checker.setContext(context);
}
for (ConditionChecker checker : expireCond.getCheckers().values()) {
checker.setContext(context);
}
for (TriggerAction action : actions) {
action.setContext(context);
}
for (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 (Exception e) {
e.printStackTrace();
logger.error("Failed to decode the trigger.", e);
throw new Exception("Failed to decode the trigger.", e);
}
return trigger;
}
public String getDescription() {
StringBuffer actionsString = new StringBuffer();
for (TriggerAction act : actions) {
actionsString.append(", ");
actionsString.append(act.getDescription());
}
return "Trigger from " + getSource() + " with trigger condition of "
+ triggerCondition.getExpression() + " and expire condition of "
+ expireCondition.getExpression() + actionsString;
}
public void stopCheckers() {
for (ConditionChecker checker : triggerCondition.getCheckers().values()) {
checker.stopChecker();
}
for (ConditionChecker checker : expireCondition.getCheckers().values()) {
checker.stopChecker();
}
}
@Override
public String toString() {
return "Trigger Id: " + getTriggerId() + ", Description: " + getDescription();
}
}