azkaban-uncached

Details

diff --git a/src/java/azkaban/scheduler/JdbcScheduleLoader.java b/src/java/azkaban/scheduler/JdbcScheduleLoader.java
index febfd09..e950938 100644
--- a/src/java/azkaban/scheduler/JdbcScheduleLoader.java
+++ b/src/java/azkaban/scheduler/JdbcScheduleLoader.java
@@ -17,12 +17,14 @@
 package azkaban.scheduler;
 
 
+import java.io.IOException;
 import java.sql.Connection;
 import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.Map;
 
 import javax.sql.DataSource;
 
@@ -36,58 +38,84 @@ import org.joda.time.ReadablePeriod;
 import org.joda.time.format.DateTimeFormat;
 import org.joda.time.format.DateTimeFormatter;
 
+import azkaban.sla.SLA;
+import azkaban.sla.SLAManagerException;
+import azkaban.sla.JdbcSLALoader.EncodingType;
 import azkaban.utils.DataSourceUtils;
+import azkaban.utils.GZIPUtils;
+import azkaban.utils.JSONUtils;
 import azkaban.utils.Props;
 
 
 public class JdbcScheduleLoader implements ScheduleLoader {
+
+	private static Logger logger = Logger.getLogger(JdbcScheduleLoader.class);
 	
-	private DataSource dataSource;
-	private static DateTimeFormatter FILE_DATEFORMAT = DateTimeFormat.forPattern("yyyy-MM-dd.HH.mm.ss.SSS");
-    private static Logger logger = Logger.getLogger(JdbcScheduleLoader.class);
+	public static enum EncodingType {
+		PLAIN(1), GZIP(2);
+
+		private int numVal;
+
+		EncodingType(int numVal) {
+			this.numVal = numVal;
+		}
+
+		public int getNumVal() {
+			return numVal;
+		}
+
+		public static EncodingType fromInteger(int x) {
+			switch (x) {
+			case 1:
+				return PLAIN;
+			case 2:
+				return GZIP;
+			default:
+				return PLAIN;
+			}
+		}
+	}
 	
-	private static final String SCHEDULE = "schedule";
-	// schedule ids
-//	private static final String PROJECTGUID = "projectGuid";
-//	private static final String FLOWGUID = "flowGuid";
-//	
-//	private static final String SCHEDULEID = "scheduleId";
-	private static final String PROJECTID = "projectId";
-	private static final String PROJECTNAME = "projectName";
-	private static final String FLOWNAME = "flowName";
-	// status
-	private static final String STATUS = "status";
-	// schedule info
-	private static final String FIRSTSCHEDTIME = "firstSchedTime";
-	private static final String TIMEZONE = "timezone";
-	private static final String PERIOD = "period";
-	private static final String LASTMODIFYTIME = "lastModifyTime";
-	private static final String NEXTEXECTIME = "nextExecTime";
-	// auditing info
-	private static final String SUBMITTIME = "submitTime";
-	private static final String SUBMITUSER = "userSubmit";
+	private DataSource dataSource;
+	private EncodingType defaultEncodingType = EncodingType.GZIP;
 	
 	private static final String scheduleTableName = "schedules";
 
-	private static String SELECT_SCHEDULE_BY_KEY = 
-			"SELECT project_id, project_name, flow_name, status, first_sched_time, timezone, period, last_modify_time, next_exec_time, submit_time, submit_user FROM " + scheduleTableName + " WHERE project_id=? AND flow_name=?";
-	
 	private static String SELECT_ALL_SCHEDULES =
-			"SELECT project_id, project_name, flow_name, status, first_sched_time, timezone, period, last_modify_time, next_exec_time, submit_time, submit_user FROM " + scheduleTableName;
+			"SELECT project_id, project_name, flow_name, status, first_sched_time, timezone, period, last_modify_time, next_exec_time, submit_time, submit_user, enc_type, schedule_options FROM " + scheduleTableName;
 	
 	private static String INSERT_SCHEDULE = 
-			"INSERT INTO " + scheduleTableName + " ( project_id, project_name, flow_name, status, first_sched_time, timezone, period, last_modify_time, next_exec_time, submit_time, submit_user) values (?,?,?,?,?,?,?,?,?,?,?)";
+			"INSERT INTO " + scheduleTableName + " ( project_id, project_name, flow_name, status, first_sched_time, timezone, period, last_modify_time, next_exec_time, submit_time, submit_user, enc_type, schedule_options) values (?,?,?,?,?,?,?,?,?,?,?,?,?)";
 	
 	private static String REMOVE_SCHEDULE_BY_KEY = 
 			"DELETE FROM " + scheduleTableName + " WHERE project_id=? AND flow_name=?";
 	
 	private static String UPDATE_SCHEDULE_BY_KEY = 
-			"UPDATE " + scheduleTableName + " SET status=?, first_sched_time=?, timezone=?, period=?, last_modify_time=?, next_exec_time=?, submit_time=?, submit_user=? WHERE project_id=? AND flow_name=?";
+			"UPDATE " + scheduleTableName + " SET status=?, first_sched_time=?, timezone=?, period=?, last_modify_time=?, next_exec_time=?, submit_time=?, submit_user=?, enc_type=?, schedule_options=? WHERE project_id=? AND flow_name=?";
 	
 	private static String UPDATE_NEXT_EXEC_TIME = 
 			"UPDATE " + scheduleTableName + " SET next_exec_time=? WHERE project_id=? AND flow_name=?";
 
+	private Connection getConnection() throws ScheduleManagerException {
+		Connection connection = null;
+		try {
+			connection = dataSource.getConnection();
+		} catch (Exception e) {
+			DbUtils.closeQuietly(connection);
+			throw new ScheduleManagerException("Error getting DB connection.", e);
+		}
+		
+		return connection;
+	}
+	
+	public EncodingType getDefaultEncodingType() {
+		return defaultEncodingType;
+	}
 
+	public void setDefaultEncodingType(EncodingType defaultEncodingType) {
+		this.defaultEncodingType = defaultEncodingType;
+	}
+	
 	public JdbcScheduleLoader(Props props) {
 		String databaseType = props.getString("database.type");
 		
@@ -106,15 +134,7 @@ public class JdbcScheduleLoader implements ScheduleLoader {
 	@Override
 	public List<Schedule> loadSchedules() throws ScheduleManagerException {
 		logger.info("Loading all schedules from db.");
-		Connection connection = null;
-		try {
-			connection = dataSource.getConnection();
-		} catch (SQLException e1) {
-			logger.error("Failed to get db connection!");
-			e1.printStackTrace();
-			DbUtils.closeQuietly(connection);
-			throw new ScheduleManagerException("Failed to get db connection!", e1);
-		}
+		Connection connection = getConnection();
 
 		QueryRunner runner = new QueryRunner();
 		ResultSetHandler<List<Schedule>> handler = new ScheduleResultHandler();
@@ -139,7 +159,7 @@ public class JdbcScheduleLoader implements ScheduleLoader {
 			}
 			else {
 				try {
-					updateNextExecTime(sched, connection);
+					updateNextExecTime(sched);
 				} catch (Exception e) {
 					DbUtils.closeQuietly(connection);
 					throw new ScheduleManagerException("Update next execution time failed.", e);
@@ -172,10 +192,29 @@ public class JdbcScheduleLoader implements ScheduleLoader {
 		}
 	}
 	
-	@Override
+	
 	public void insertSchedule(Schedule s) throws ScheduleManagerException {
 		logger.info("Inserting schedule " + s.getScheduleName() + " into db.");
+		insertSchedule(s, defaultEncodingType);
+	}
 
+	public void insertSchedule(Schedule s, EncodingType encType) throws ScheduleManagerException {
+		
+		String json = JSONUtils.toJSON(s.optionToObject());
+		byte[] data = null;
+		try {
+			byte[] stringData = json.getBytes("UTF-8");
+			data = stringData;
+	
+			if (encType == EncodingType.GZIP) {
+				data = GZIPUtils.gzipBytes(stringData);
+			}
+			logger.debug("NumChars: " + json.length() + " UTF-8:" + stringData.length + " Gzip:"+ data.length);
+		}
+		catch (IOException e) {
+			throw new ScheduleManagerException("Error encoding the schedule options" + s.getSchedOptions());
+		}
+		
 		QueryRunner runner = new QueryRunner(dataSource);
 		try {
 			int inserts =  runner.update( 
@@ -190,7 +229,9 @@ public class JdbcScheduleLoader implements ScheduleLoader {
 					s.getLastModifyTime(), 
 					s.getNextExecTime(), 
 					s.getSubmitTime(), 
-					s.getSubmitUser());
+					s.getSubmitUser(),
+					encType.getNumVal(),
+					data);
 			if (inserts == 0) {
 				throw new ScheduleManagerException("No schedule has been inserted.");
 			}
@@ -200,8 +241,10 @@ public class JdbcScheduleLoader implements ScheduleLoader {
 		}
 	}
 	
-	private void updateNextExecTime(Schedule s, Connection connection) throws ScheduleManagerException 
+	@Override
+	public void updateNextExecTime(Schedule s) throws ScheduleManagerException 
 	{
+		Connection connection = getConnection();
 		QueryRunner runner = new QueryRunner();
 		try {
 			int updates = runner.update(connection, UPDATE_NEXT_EXEC_TIME, s.getNextExecTime(), s.getProjectId(), s.getFlowName()); 
@@ -214,6 +257,25 @@ public class JdbcScheduleLoader implements ScheduleLoader {
 	@Override
 	public void updateSchedule(Schedule s) throws ScheduleManagerException {
 		logger.info("Updating schedule " + s.getScheduleName() + " into db.");
+		updateSchedule(s, defaultEncodingType);
+	}
+		
+	public void updateSchedule(Schedule s, EncodingType encType) throws ScheduleManagerException {
+
+		String json = JSONUtils.toJSON(s.optionToObject());
+		byte[] data = null;
+		try {
+			byte[] stringData = json.getBytes("UTF-8");
+			data = stringData;
+	
+			if (encType == EncodingType.GZIP) {
+				data = GZIPUtils.gzipBytes(stringData);
+			}
+			logger.debug("NumChars: " + json.length() + " UTF-8:" + stringData.length + " Gzip:"+ data.length);
+		}
+		catch (IOException e) {
+			throw new ScheduleManagerException("Error encoding the schedule options" + s.getSchedOptions());
+		}
 
 		QueryRunner runner = new QueryRunner(dataSource);
 	
@@ -227,7 +289,9 @@ public class JdbcScheduleLoader implements ScheduleLoader {
 					s.getLastModifyTime(), 
 					s.getNextExecTime(), 
 					s.getSubmitTime(), 
-					s.getSubmitUser(), 
+					s.getSubmitUser(), 	
+					encType.getNumVal(),
+					data,
 					s.getProjectId(), 
 					s.getFlowName());
 			if (updates == 0) {
@@ -238,46 +302,6 @@ public class JdbcScheduleLoader implements ScheduleLoader {
 			throw new ScheduleManagerException("Update schedule " + s.getScheduleName() + " into db failed. ", e);
 		}
 	}
-	
-//	private Schedule fromJSON(HashMap<String, Object> obj) {
-//		long projectGuid = Long.valueOf((String) obj.get(PROJECTGUID));
-//		String projectId = (String) obj.get(PROJECTID);
-//		long flowGuid = Long.valueOf((String) obj.get(FLOWGUID));
-//		String flowId = (String) obj.get(FLOWID);
-//		String status = (String) obj.get(STATUS);
-//		long firstSchedTime = Long.valueOf((String) obj.get(FIRSTSCHEDTIME));
-//		String timezone = (String) obj.get(TIMEZONE);
-//		String period = (String) obj.get(PERIOD);
-//		long lastModifyTime = Long.valueOf((String) obj.get(LASTMODIFYTIME));
-//		long nextExecTime = Long.valueOf((String) obj.get(NEXTEXECTIME));
-//		long submitTime = Long.valueOf((String) obj.get(SUBMITTIME));
-//		String submitUser = (String) obj.get(SUBMITUSER);
-//		
-//		return new Schedule(projectId, flowId, status, firstSchedTime, timezone, period, lastModifyTime, nextExecTime, submitTime, submitUser);
-//
-//	}
-//
-//	private HashMap<String, Object> toJSON(Schedule s) {
-//		HashMap<String, Object> object = new HashMap<String, Object>();
-////		object.put(PROJECTGUID, s.getProjectGuid());
-//		object.put(SCHEDULEID, s.getScheduleId());
-//		object.put(PROJECTID, s.getProjectId());
-////		object.put(FLOWGUID, s.getFlowGuid());
-//		object.put(FLOWID, s.getFlowId());
-//		
-//		object.put(STATUS, s.getStatus());
-//		
-//		object.put(FIRSTSCHEDTIME, s.getFirstSchedTime());
-//		object.put(TIMEZONE, s.getTimezone());
-//		object.put(PERIOD, s.getPeriod());
-//		
-//		object.put(LASTMODIFYTIME, s.getLastModifyTime());
-//		object.put(NEXTEXECTIME, s.getNextExecTime());
-//		object.put(SUBMITTIME, s.getSubmitTime());
-//		object.put(SUBMITUSER, s.getSubmitUser());
-//
-//		return object;
-//	}
 
 	public class ScheduleResultHandler implements ResultSetHandler<List<Schedule>> {
 		@Override
@@ -299,8 +323,31 @@ public class JdbcScheduleLoader implements ScheduleLoader {
 				long nextExecTime = rs.getLong(9);
 				long submitTime = rs.getLong(10);
 				String submitUser = rs.getString(11);
+				int encodingType = rs.getInt(12);
+				byte[] data = rs.getBytes(13);
+				
+				Map<String, Object> options = null;
+				if (data != null) {
+					EncodingType encType = EncodingType.fromInteger(encodingType);
+					Object optsObj;
+					try {
+						// Convoluted way to inflate strings. Should find common package or helper function.
+						if (encType == EncodingType.GZIP) {
+							// Decompress the sucker.
+							String jsonString = GZIPUtils.unGzipString(data, "UTF-8");
+							optsObj = JSONUtils.parseJSONFromString(jsonString);
+						}
+						else {
+							String jsonString = new String(data, "UTF-8");
+							optsObj = JSONUtils.parseJSONFromString(jsonString);
+						}						
+						options = Schedule.createScheduleOptionFromObject(optsObj);
+					} catch (IOException e) {
+						throw new SQLException("Error reconstructing schedule options " + projectName + "." + flowName);
+					}
+				}
 				
-				Schedule s = new Schedule(projectId, projectName, flowName, status, firstSchedTime, timezone, period, lastModifyTime, nextExecTime, submitTime, submitUser);
+				Schedule s = new Schedule(projectId, projectName, flowName, status, firstSchedTime, timezone, period, lastModifyTime, nextExecTime, submitTime, submitUser, options);
 				
 				schedules.add(s);
 			} while (rs.next());
diff --git a/src/java/azkaban/scheduler/Schedule.java b/src/java/azkaban/scheduler/Schedule.java
index a8c0c91..98b6973 100644
--- a/src/java/azkaban/scheduler/Schedule.java
+++ b/src/java/azkaban/scheduler/Schedule.java
@@ -18,6 +18,11 @@ package azkaban.scheduler;
 
 
 
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
 import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
 import org.joda.time.Days;
@@ -50,6 +55,7 @@ public class Schedule{
 	private String submitUser;
 	private String status;
 	private long submitTime;
+	private Map<String, Object> schedOptions;
 	
 	public Schedule(
 						int projectId,
@@ -62,7 +68,8 @@ public class Schedule{
 						long lastModifyTime,						
 						long nextExecTime,						
 						long submitTime,
-						String submitUser
+						String submitUser,
+						Map<String, Object> schedOptions
 						) {
 		this.projectId = projectId;
 		this.projectName = projectName;
@@ -75,31 +82,43 @@ public class Schedule{
 		this.submitUser = submitUser;
 		this.status = status;
 		this.submitTime = submitTime;
+		this.schedOptions = schedOptions;
 	}
-	
+
 	public Schedule(
-			int projectId,
-			String projectName,
-			String flowName,
-			String status,
-			long firstSchedTime,
-			String timezone,
-			String period,
-			long lastModifyTime,						
-			long nextExecTime,						
-			long submitTime,
-			String submitUser) {
+						int projectId,
+						String projectName,
+						String flowName,
+						String status,
+						long firstSchedTime,
+						String timezoneId,
+						String period,
+						long lastModifyTime,						
+						long nextExecTime,						
+						long submitTime,
+						String submitUser,
+						Map<String, Object> schedOptions
+			) {
 		this.projectId = projectId;
 		this.projectName = projectName;
 		this.flowName = flowName;
 		this.firstSchedTime = firstSchedTime;
-		this.timezone = DateTimeZone.forID(timezone);
+		this.timezone = DateTimeZone.forID(timezoneId);
 		this.lastModifyTime = lastModifyTime;
 		this.period = parsePeriodString(period);
 		this.nextExecTime = nextExecTime;
 		this.submitUser = submitUser;
 		this.status = status;
 		this.submitTime = submitTime;
+		this.schedOptions = schedOptions;
+	}
+
+	public Map<String, Object> getSchedOptions() {
+		return schedOptions;
+	}
+
+	public void setSchedOptions(Map<String, Object> schedOptions) {
+		this.schedOptions = schedOptions;
 	}
 
 	public String getScheduleName() {
@@ -155,85 +174,77 @@ public class Schedule{
 	}
 
 	public boolean updateTime() {
-        if (new DateTime(nextExecTime).isAfterNow()) {
-            return true;
-        }
-
-        if (period != null) {
-            DateTime nextTime = getNextRuntime(nextExecTime, timezone, period);
-
-            this.nextExecTime = nextTime.getMillis();
-            return true;
-        }
-
-        return false;
-    }
-//	public String getFlowId(){
-//		return this.scheduleId.split("\\.")[1];
-//	}
-//
-//	public String getProjectId(){
-//		return this.scheduleId.split("\\.")[0];
-//	}
-	
+		if (new DateTime(nextExecTime).isAfterNow()) {
+			return true;
+		}
+
+		if (period != null) {
+			DateTime nextTime = getNextRuntime(nextExecTime, timezone, period);
+
+			this.nextExecTime = nextTime.getMillis();
+			return true;
+		}
+
+		return false;
+	}
 	
-    private DateTime getNextRuntime(long scheduleTime, DateTimeZone timezone, ReadablePeriod period) {
-        DateTime now = new DateTime();
-        DateTime date = new DateTime(scheduleTime).withZone(timezone);
-        int count = 0;
-        while (!now.isBefore(date)) {
-            if (count > 100000) {
-                throw new IllegalStateException(
-                        "100000 increments of period did not get to present time.");
-            }
-
-            if (period == null) {
-                break;
-            } else {
-                date = date.plus(period);
-            }
-
-            count += 1;
-        }
-
-        return date;
-    }
-
-    public static ReadablePeriod parsePeriodString(String periodStr) {
-        ReadablePeriod period;
-        char periodUnit = periodStr.charAt(periodStr.length() - 1);
-        if (periodUnit == 'n') {
-            return null;
-        }
-
-        int periodInt = Integer.parseInt(periodStr.substring(0,
-                periodStr.length() - 1));
-        switch (periodUnit) {
-        case 'M':
-            period = Months.months(periodInt);
-            break;
-        case 'w':
-            period = Weeks.weeks(periodInt);
-            break;
-        case 'd':
-            period = Days.days(periodInt);
-            break;
-        case 'h':
-            period = Hours.hours(periodInt);
-            break;
-        case 'm':
-            period = Minutes.minutes(periodInt);
-            break;
-        case 's':
-            period = Seconds.seconds(periodInt);
-            break;
-        default:
-            throw new IllegalArgumentException("Invalid schedule period unit '"
-                    + periodUnit);
-        }
-
-        return period;
-    }
+	private DateTime getNextRuntime(long scheduleTime, DateTimeZone timezone, ReadablePeriod period) {
+		DateTime now = new DateTime();
+		DateTime date = new DateTime(scheduleTime).withZone(timezone);
+		int count = 0;
+		while (!now.isBefore(date)) {
+			if (count > 100000) {
+				throw new IllegalStateException(
+						"100000 increments of period did not get to present time.");
+			}
+
+			if (period == null) {
+				break;
+			} else {
+				date = date.plus(period);
+			}
+
+			count += 1;
+		}
+
+		return date;
+	}
+
+	public static ReadablePeriod parsePeriodString(String periodStr) {
+		ReadablePeriod period;
+		char periodUnit = periodStr.charAt(periodStr.length() - 1);
+		if (periodUnit == 'n') {
+			return null;
+		}
+
+		int periodInt = Integer.parseInt(periodStr.substring(0,
+				periodStr.length() - 1));
+		switch (periodUnit) {
+		case 'M':
+			period = Months.months(periodInt);
+			break;
+		case 'w':
+			period = Weeks.weeks(periodInt);
+			break;
+		case 'd':
+			period = Days.days(periodInt);
+			break;
+		case 'h':
+			period = Hours.hours(periodInt);
+			break;
+		case 'm':
+			period = Minutes.minutes(periodInt);
+			break;
+		case 's':
+			period = Seconds.seconds(periodInt);
+			break;
+		default:
+			throw new IllegalArgumentException("Invalid schedule period unit '"
+					+ periodUnit);
+		}
+
+		return period;
+	}
 
 	public static String createPeriodString(ReadablePeriod period) {
 		String periodStr = "n";
@@ -264,7 +275,36 @@ public class Schedule{
 
 		return periodStr;
 	}
-    
+	
 
+	public Map<String,Object> optionToObject() {
+		//HashMap<String, Object> optionObject = new HashMap<String, Object>();
+		
+
+		return schedOptions;
+	}
+	
+	@SuppressWarnings("unchecked")
+	public static Map<String, Object> createScheduleOptionFromObject(Object obj) {
+		Map<String, Object> options = new HashMap<String, Object>();
+		if(obj != null) {
+			HashMap<String, Object> optionObject = (HashMap<String,Object>)obj;
+			options.putAll(optionObject);
+			return options;
+		}
+		else {
+			return new HashMap<String, Object>();
+		}
+	}
+
+	@SuppressWarnings("unchecked")
+	public List<String> getDisabledJobs() {
+		if (schedOptions.containsKey("disabled")) {
+			return (List<String>) schedOptions.get("disabled");
+		}
+		else {
+			return new ArrayList<String>();
+		}
+	}
 	
 }
\ No newline at end of file
diff --git a/src/java/azkaban/scheduler/ScheduleLoader.java b/src/java/azkaban/scheduler/ScheduleLoader.java
index 4b55b4e..9caed47 100644
--- a/src/java/azkaban/scheduler/ScheduleLoader.java
+++ b/src/java/azkaban/scheduler/ScheduleLoader.java
@@ -16,6 +16,7 @@
 
 package azkaban.scheduler;
 
+import java.sql.Connection;
 import java.util.List;
 
 public interface ScheduleLoader {
@@ -28,4 +29,6 @@ public interface ScheduleLoader {
 	
 	public void removeSchedule(Schedule s) throws ScheduleManagerException;
 
+	public void updateNextExecTime(Schedule s) throws ScheduleManagerException;
+
 }
\ No newline at end of file
diff --git a/src/java/azkaban/scheduler/ScheduleManager.java b/src/java/azkaban/scheduler/ScheduleManager.java
index ef0f076..9c3c459 100644
--- a/src/java/azkaban/scheduler/ScheduleManager.java
+++ b/src/java/azkaban/scheduler/ScheduleManager.java
@@ -17,6 +17,7 @@
 package azkaban.scheduler;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Comparator;
 import java.util.LinkedHashMap;
 import java.util.List;
@@ -34,6 +35,8 @@ import org.joda.time.format.DateTimeFormatter;
 import azkaban.executor.ExecutableFlow;
 import azkaban.executor.ExecutorManager;
 import azkaban.executor.ExecutorManagerException;
+import azkaban.executor.ExecutableFlow.FailureAction;
+import azkaban.executor.ExecutableFlow.Status;
 
 import azkaban.flow.Flow;
 import azkaban.jobExecutor.utils.JobExecutionException;
@@ -169,9 +172,10 @@ public class ScheduleManager {
 			final long lastModifyTime,
 			final long nextExecTime,
 			final long submitTime,
-			final String submitUser
+			final String submitUser,
+			final Map<String, Object> options
 			) {
-		Schedule sched = new Schedule(projectId, projectName, flowName, status, firstSchedTime, timezone, period, lastModifyTime, nextExecTime, submitTime, submitUser);
+		Schedule sched = new Schedule(projectId, projectName, flowName, status, firstSchedTime, timezone, period, lastModifyTime, nextExecTime, submitTime, submitUser, options);
 		logger.info("Scheduling flow '" + sched.getScheduleName() + "' for "
 				+ _dateFormat.print(firstSchedTime) + " with a period of "
 				+ period == null ? "(non-recurring)" : period);
@@ -362,18 +366,57 @@ public class ScheduleManager {
 										// Create ExecutableFlow
 										ExecutableFlow exflow = new ExecutableFlow(flow);
 										exflow.setSubmitUser(runningSched.getSubmitUser());
+										
+										Map<String, Object> scheduleOptions = runningSched.getSchedOptions();
+										
+										if(scheduleOptions != null && scheduleOptions.containsKey("flowOptions")) {
+											Map<String, Object> flowOptions = (Map<String, Object>) scheduleOptions.get("flowOptions");
+										
+											if (flowOptions.containsKey("failureAction")) {
+												String option = (String) flowOptions.get("failureAction");
+												if (option.equals("finishCurrent") ) {
+													exflow.setFailureAction(FailureAction.FINISH_CURRENTLY_RUNNING);
+												}
+												else if (option.equals("cancelImmediately")) {
+													exflow.setFailureAction(FailureAction.CANCEL_ALL);
+												}
+												else if (option.equals("finishPossible")) {
+													exflow.setFailureAction(FailureAction.FINISH_ALL_POSSIBLE);
+												}
+											}
 	
-										// TODO make disabled in scheduled flow
-										// Map<String, String> paramGroup =
-										// this.getParamGroup(req, "disabled");
-										// for (Map.Entry<String, String> entry:
-										// paramGroup.entrySet()) {
-										// boolean nodeDisabled =
-										// Boolean.parseBoolean(entry.getValue());
-										// exflow.setStatus(entry.getKey(),
-										// nodeDisabled ? Status.DISABLED :
-										// Status.READY);
-										// }
+											if (flowOptions.containsKey("failureEmails")) {
+												String emails = (String) flowOptions.get("failureEmails");
+												String[] emailSplit = emails.split("\\s*,\\s*|\\s*;\\s*|\\s+");
+												exflow.setFailureEmails(Arrays.asList(emailSplit));
+											}
+											if (flowOptions.containsKey("successEmails")) {
+												String emails = (String) flowOptions.get("successEmails");
+												String[] emailSplit = emails.split("\\s*,\\s*|\\s*;\\s*|\\s+");
+												exflow.setSuccessEmails(Arrays.asList(emailSplit));
+											}
+											if (flowOptions.containsKey("notifyFailureFirst")) {
+												exflow.setNotifyOnFirstFailure(Boolean.parseBoolean((String)flowOptions.get("notifyFailureFirst")));
+											}
+											if (flowOptions.containsKey("notifyFailureLast")) {
+												exflow.setNotifyOnLastFailure(Boolean.parseBoolean((String)flowOptions.get("notifyFailureLast")));
+											}
+											if (flowOptions.containsKey("executingJobOption")) {
+												String option = (String)flowOptions.get("jobOption");
+												// Not set yet
+											}
+											
+											Map<String, String> flowParamGroup = this.getParamGroup(req, "flowOverride");
+											exflow.addFlowParameters(flowParamGroup);
+											
+											// Setup disabled
+											Map<String, String> paramGroup = this.getParamGroup(req, "disable");
+											for (Map.Entry<String, String> entry: paramGroup.entrySet()) {
+												boolean nodeDisabled = Boolean.parseBoolean(entry.getValue());
+												exflow.setStatus(entry.getKey(), nodeDisabled ? Status.DISABLED : Status.READY);
+											}
+											
+										}
 	
 										try {
 											executorManager.submitExecutableFlow(exflow);
diff --git a/src/java/azkaban/utils/EmailMessage.java b/src/java/azkaban/utils/EmailMessage.java
index 1ac7607..b510d6c 100644
--- a/src/java/azkaban/utils/EmailMessage.java
+++ b/src/java/azkaban/utils/EmailMessage.java
@@ -21,7 +21,10 @@ import javax.mail.internet.MimeBodyPart;
 import javax.mail.internet.MimeMessage;
 import javax.mail.internet.MimeMultipart;
 
+import com.sun.mail.smtp.SMTPTransport;
+
 public class EmailMessage {
+	private static String protocol = "smtp";
 	private List<String> _toAddress = new ArrayList<String>();
 	private String _mailHost;
 	private String _mailUser;
@@ -129,12 +132,13 @@ public class EmailMessage {
 	public void sendEmail() throws MessagingException {
 		checkSettings();
 		Properties props = new Properties();
-		props.setProperty("mail.transport.protocol", "smtp");
-		props.put("mail.host", _mailHost);
+//		props.setProperty("mail.transport.protocol", "smtp");
+		props.put("mail."+protocol+".host", _mailHost);
+		props.put("mail."+protocol+".auth", "true");
 		props.put("mail.user", _mailUser);
 		props.put("mail.password", _mailPassword);
 
-		Session session = Session.getDefaultInstance(props);
+		Session session = Session.getInstance(props, null);
 		Message message = new MimeMessage(session);
 		InternetAddress from = new InternetAddress(_fromAddress, false);
 		message.setFrom(from);
@@ -160,11 +164,13 @@ public class EmailMessage {
 			message.setContent(_body.toString(), _mimeType);
 		}
 
-		Transport transport = session.getTransport();
-		transport.connect();
-		transport.sendMessage(message,
+//		Transport transport = session.getTransport();
+		
+		SMTPTransport t = (SMTPTransport) session.getTransport(protocol);
+		t.connect(_mailHost, _mailUser, _mailPassword);
+		t.sendMessage(message,
 				message.getRecipients(Message.RecipientType.TO));
-		transport.close();
+		t.close();
 	}
 
 	public void setBody(String body) {
diff --git a/src/java/azkaban/webapp/AzkabanWebServer.java b/src/java/azkaban/webapp/AzkabanWebServer.java
index a11f8cb..42c5a06 100644
--- a/src/java/azkaban/webapp/AzkabanWebServer.java
+++ b/src/java/azkaban/webapp/AzkabanWebServer.java
@@ -53,6 +53,8 @@ import azkaban.project.ProjectManager;
 
 import azkaban.scheduler.JdbcScheduleLoader;
 import azkaban.scheduler.ScheduleManager;
+import azkaban.sla.JdbcSLALoader;
+import azkaban.sla.SLAManager;
 import azkaban.user.UserManager;
 import azkaban.user.XmlUserManager;
 import azkaban.utils.FileIOUtils;
@@ -121,6 +123,7 @@ public class AzkabanWebServer implements AzkabanServer {
 	private ProjectManager projectManager;
 	private ExecutorManager executorManager;
 	private ScheduleManager scheduleManager;
+	private SLAManager slaManager;
 
 	private final ClassLoader baseClassLoader;
 	
@@ -148,6 +151,7 @@ public class AzkabanWebServer implements AzkabanServer {
 		projectManager = loadProjectManager(props);
 		executorManager = loadExecutorManager(props);
 		scheduleManager = loadScheduleManager(executorManager, props);
+		slaManager = loadSLAManager();
 		baseClassLoader = getBaseClassloader();
 		
 		tempDir = new File(props.getString("azkaban.temp.dir", "temp"));
@@ -162,6 +166,8 @@ public class AzkabanWebServer implements AzkabanServer {
 		}
 	}
 	
+	
+
 	private void setViewerPlugins(List<ViewerPlugin> viewerPlugins) {
 		this.viewerPlugins = viewerPlugins;
 	}
@@ -211,6 +217,11 @@ public class AzkabanWebServer implements AzkabanServer {
 		return schedManager;
 	}
 
+	private SLAManager loadSLAManager() {
+		SLAManager slaManager = new SLAManager(executorManager, projectManager, new JdbcSLALoader(props));
+		return slaManager;
+	}
+	
 	/**
 	 * Returns the web session cache.
 	 * 
@@ -252,6 +263,10 @@ public class AzkabanWebServer implements AzkabanServer {
 		return executorManager;
 	}
 	
+	public SLAManager getSLAManager() {
+		return slaManager;
+	}
+	
 	public ScheduleManager getScheduleManager() {
 		return scheduleManager;
 	}
@@ -654,4 +669,6 @@ public class AzkabanWebServer implements AzkabanServer {
 		
 		return props;
 	}
+
+	
 }
diff --git a/src/java/azkaban/webapp/servlet/ScheduleServlet.java b/src/java/azkaban/webapp/servlet/ScheduleServlet.java
index 480ea9a..40214f7 100644
--- a/src/java/azkaban/webapp/servlet/ScheduleServlet.java
+++ b/src/java/azkaban/webapp/servlet/ScheduleServlet.java
@@ -17,6 +17,7 @@
 package azkaban.webapp.servlet;
 
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -25,15 +26,21 @@ import javax.servlet.ServletConfig;
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
+import javax.swing.text.StyledEditorKit.BoldAction;
 
 import org.apache.log4j.Logger;
 import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
+import org.joda.time.Hours;
 import org.joda.time.LocalDateTime;
+import org.joda.time.Minutes;
 import org.joda.time.ReadablePeriod;
 import org.joda.time.format.DateTimeFormat;
 
+import azkaban.executor.ExecutableFlow;
+import azkaban.executor.ExecutorManagerException;
 import azkaban.flow.Flow;
+import azkaban.flow.Node;
 import azkaban.project.Project;
 import azkaban.project.ProjectManager;
 import azkaban.project.ProjectLogEvent.EventType;
@@ -47,12 +54,18 @@ import azkaban.webapp.AzkabanWebServer;
 import azkaban.webapp.session.Session;
 import azkaban.scheduler.Schedule;
 import azkaban.scheduler.ScheduleManager;
+import azkaban.sla.FlowRule;
+import azkaban.sla.JobRule;
+import azkaban.sla.SLA;
+import azkaban.sla.SLAManager;
+import azkaban.sla.SLA.SlaAction;
 
 public class ScheduleServlet extends LoginAbstractAzkabanServlet {
 	private static final long serialVersionUID = 1L;
 	private static final Logger logger = Logger.getLogger(ScheduleServlet.class);
 	private ProjectManager projectManager;
 	private ScheduleManager scheduleManager;
+	private SLAManager slaManager;
 	private UserManager userManager;
 
 	@Override
@@ -62,15 +75,211 @@ public class ScheduleServlet extends LoginAbstractAzkabanServlet {
 		projectManager = server.getProjectManager();
 		scheduleManager = server.getScheduleManager();
 		userManager = server.getUserManager();
+		slaManager = server.getSLAManager();
 	}
 	
 	@Override
 	protected void handleGet(HttpServletRequest req, HttpServletResponse resp,
 			Session session) throws ServletException, IOException {
+		if (hasParam(req, "ajax")) {
+			handleAJAXAction(req, resp, session);
+		}
+		else {
+			handleGetAllSchedules(req, resp, session);
+		}
+	}
+	
+	private void handleAJAXAction(HttpServletRequest req, HttpServletResponse resp, Session session) throws ServletException, IOException {
+		HashMap<String, Object> ret = new HashMap<String, Object>();
+		String ajaxName = getParam(req, "ajax");
+		
+		if (ajaxName.equals("schedInfo")) {
+			ajaxSchedInfo(req, resp, ret, session.getUser());
+		}
+		else if(ajaxName.equals("setSla")) {
+			ajaxSetSla(req, resp, ret, session.getUser());
+		}
+
+		if (ret != null) {
+			this.writeJSON(resp, ret);
+		}
+	}
+
+	private void ajaxSetSla(HttpServletRequest req, HttpServletResponse resp, HashMap<String, Object> ret, User user) {
+		try {
+			
+			int projectId = getIntParam(req, "projectId");
+			String flowName = getParam(req, "flowName");
+			
+			Project project = projectManager.getProject(projectId);
+			if(!hasPermission(project, user, Permission.Type.SCHEDULE)) {
+				ret.put("error", "User " + user + " does not have permission to set SLA for this flow.");
+				return;
+			}
+			
+			String slaEmals = getParam(req, "slaEmails");
+			System.out.println(slaEmals);			
+
+			String flowRules = getParam(req, "flowRules");
+			FlowRule flowRule = parseFlowRule(flowRules);
+			
+			List<JobRule> jobRule = new ArrayList<JobRule>();
+			Map<String, String> jobRules = getParamGroup(req, "jobRules");
+			System.out.println(jobRules);
+			for(String job : jobRules.keySet()) {
+				JobRule jr = parseJobRule(job, jobRules.get(job));
+				jobRule.add(jr);
+			}
+			Map<String, Object> options= new HashMap<String, Object>();
+			options.put("slaEmails", slaEmals);
+			options.put("flowRules", flowRules);
+			options.put("jobRules", jobRule);
+			Schedule sched = scheduleManager.getSchedule(new Pair<Integer, String>(projectId, flowName));
+			//slaManager.addFlowSLA(projectId, project.getName(), flowName, "ready", sched.getFirstSchedTime(), sched.getTimezone(), sched.getPeriod(), DateTime.now(), DateTime.now(), DateTime.now(), user, options);
+		
+		} catch (ServletException e) {
+			ret.put("error", e);
+		}
+		
+	}
+
+	
+	private FlowRule parseFlowRule(String flowRules) {
+		String[] parts = flowRules.split(",");
+		String duration = parts[0];
+		String emailAction = parts[1];
+		String killAction = parts[2];
+		if(emailAction.equals("on") || killAction.equals("on")) {
+			if(!duration.equals("")) {
+				FlowRule r = new FlowRule();
+				ReadablePeriod dur = parseDuration(duration);
+				r.setDuration(dur);
+				List<SlaAction> actions = new ArrayList<SLA.SlaAction>();
+				if(emailAction.equals("on")) {
+					actions.add(SlaAction.SENDEMAIL);
+				}
+				if(killAction.equals("on")) {
+					actions.add(SlaAction.KILL);
+				}
+				r.setActions(actions);
+				return r;
+			}
+		}		
+		return null;
+	}
+
+	private JobRule parseJobRule(String job, String jobRule) {
+		String[] parts = jobRule.split(",");
+		String duration = parts[0];
+		String emailAction = parts[1];
+		String killAction = parts[2];
+		if(emailAction.equals("on") || killAction.equals("on")) {
+			if(!duration.equals("")) {
+				JobRule r = new JobRule();
+				r.setJobId(job);
+				ReadablePeriod dur = parseDuration(duration);
+				r.setDuration(dur);
+				List<SlaAction> actions = new ArrayList<SLA.SlaAction>();
+				if(emailAction.equals("on")) {
+					actions.add(SlaAction.SENDEMAIL);
+				}
+				if(killAction.equals("on")) {
+					actions.add(SlaAction.KILL);
+				}
+				r.setActions(actions);
+				return r;
+			}
+		}	
+		return null;
+	}
+
+	private ReadablePeriod parseDuration(String duration) {
+		int hour = Integer.parseInt(duration.split(",")[0]);
+		int min = Integer.parseInt(duration.split(",")[1]);
+		return Hours.hours(hour).toPeriod().plus(Minutes.minutes(min).toPeriod());
+	}
+
+	@SuppressWarnings("unchecked")
+	private void ajaxSchedInfo(HttpServletRequest req, HttpServletResponse resp, HashMap<String, Object> ret, User user) {
+		int projId;
+		try {
+			projId = getIntParam(req, "projId");
+			String flowName = getParam(req, "flowName");
+			
+			Project project = getProjectAjaxByPermission(ret, projId, user, Type.READ);
+			if (project == null) {
+				ret.put("error", "Error loading project. Project " + projId + " doesn't exist");
+				return;
+			}
+			
+			Flow flow = project.getFlow(flowName);
+			if (flow == null) {
+				ret.put("error", "Error loading flow. Flow " + flowName + " doesn't exist in " + projId);
+				return;
+			}
+			
+			SLA sla = slaManager.getSLA(new Pair<Integer, String>(projId, flowName));
+			
+			if(sla != null) {
+				ret.put("slaEmails", (List<String>)sla.getSlaOptions().get("slaEmails"));
+				List<String> allJobs = new ArrayList<String>();
+				for(Node n : flow.getNodes()) {
+					allJobs.add(n.getId());
+				}
+				ret.put("allJobs", allJobs);
+				if(sla.getFlowRules() != null) {
+					ret.put("flowRules", sla.getFlowRules());
+				}
+				if(sla.getJobRules() != null) {
+					ret.put("jobRules", sla.getJobRules());
+				}
+			}
+			else {
+				ret.put("slaEmails", flow.getFailureEmails());
+				List<String> allJobs = new ArrayList<String>();
+				Schedule sched = scheduleManager.getSchedule(new Pair<Integer, String>(projId, flowName));
+				List<String> disabled = sched.getDisabledJobs(); 
+				for(Node n : flow.getNodes()) {
+					if(!disabled.contains(n.getId())) {
+						allJobs.add(n.getId());
+					}
+				}
+				ret.put("allJobs", allJobs);
+				
+				
+			}
+		} catch (ServletException e) {
+			ret.put("error", e);
+		}
+		
+	}
+
+	protected Project getProjectAjaxByPermission(Map<String, Object> ret, int projectId, User user, Permission.Type type) {
+		Project project = projectManager.getProject(projectId);
+		
+		if (project == null) {
+			ret.put("error", "Project '" + project + "' not found.");
+		}
+		else if (!hasPermission(project, user, type)) {
+			ret.put("error", "User '" + user.getUserId() + "' doesn't have " + type.name() + " permissions on " + project.getName());
+		}
+		else {
+			return project;
+		}
+		
+		return null;
+	}
+	
+	private void handleGetAllSchedules(HttpServletRequest req, HttpServletResponse resp,
+			Session session) throws ServletException, IOException{
+		
 		Page page = newPage(req, resp, session, "azkaban/webapp/servlet/velocity/scheduledflowpage.vm");
 		
 		List<Schedule> schedules = scheduleManager.getSchedules();
 		page.add("schedules", schedules);
+		
+		List<SLA> slas = slaManager.getSLAs();
+		page.add("slas", slas);
 
 		page.render();
 	}
@@ -205,7 +414,7 @@ public class ScheduleServlet extends LoginAbstractAzkabanServlet {
 		
 		//ScheduledFlow schedFlow = scheduleManager.schedule(scheduleId, projectId, flowId, userExec, userSubmit, submitTime, firstSchedTime, thePeriod);
 		//project.info("User '" + user.getUserId() + "' has scheduled " + flow.getId() + "[" + schedFlow.toNiceString() + "].");
-		Schedule schedule = scheduleManager.scheduleFlow(projectId, projectName, flowName, "ready", firstSchedTime.getMillis(), timezone, thePeriod, submitTime.getMillis(), firstSchedTime.getMillis(), firstSchedTime.getMillis(), user.getUserId());
+		Schedule schedule = scheduleManager.scheduleFlow(projectId, projectName, flowName, "ready", firstSchedTime.getMillis(), timezone, thePeriod, submitTime.getMillis(), firstSchedTime.getMillis(), firstSchedTime.getMillis(), user.getUserId(), null);
 		logger.info("User '" + user.getUserId() + "' has scheduled " + "[" + projectName + flowName +  " (" + projectId +")" + "].");
 		projectManager.postProjectEvent(project, EventType.SCHEDULE, user.getUserId(), "Schedule " + schedule.getScheduleName() + " has been added.");
 		
diff --git a/src/java/azkaban/webapp/servlet/velocity/flowpage.vm b/src/java/azkaban/webapp/servlet/velocity/flowpage.vm
index ee2436b..b4e1355 100644
--- a/src/java/azkaban/webapp/servlet/velocity/flowpage.vm
+++ b/src/java/azkaban/webapp/servlet/velocity/flowpage.vm
@@ -131,7 +131,166 @@
 					</div>
 				</div>
 		<!-- modal content -->
-                <div id="schedule-flow" class="modal">
+				<div id="advScheduleModalBackground" class="modalBackground2">
+					<div id="schedule-options" class="modal modalContainer2">
+						<a href='#' title='Close' class='modal-close'>x</a>
+							<h3>Advanced Schedule Options</h3>
+							<div>
+								<ul class="optionsPicker">
+									<li id="scheduleGeneralOptions">General Options</li>
+									<li id="scheduleFlowOptions">Flow Options</li>
+									<li id="scheduleSlaOptions">SLA Options</li>
+								</ul>
+							</div>
+							<div class="optionsPane">
+						<div id="scheduleSlaPanel" class="generalPanel panel">
+							<div id="slaActions">
+								<h4>SLA Alert Emails</h4>
+								<dl>
+									<dt >SLA Alert Emails</dt>
+									<dd>
+										<textarea id="slaEmails"></textarea>
+									</dd>
+								</dl>
+							</div>
+							<div id="slaRules">
+								<h4>Flow SLA Rules</h4>
+								<div class="tableDiv">
+									<table id="flowRulesTbl">
+										<thead>
+											<tr>
+												<th>Flow/Job</th>
+												<th>Finish In</th>
+												<th>Email Action</th>
+												<th>Kill Action</th>
+											</tr>
+										</thead>
+										<tbody>
+										</tbody>
+									</table>
+								</div>
+								<h4>Job SLA Rules</h4>
+								<div class="tableDiv">
+									<table id="jobRulesTbl">
+										<thead>
+											<tr>
+												<th>Flow/Job</th>
+												<th>Finish In</th>
+												<th>Email Action</th>
+												<th>Kill Action</th>
+											</tr>
+										</thead>
+										<tbody>
+										</tbody>
+									</table>
+								</div>
+							</div>
+						</div>
+						<div id="scheduleGeneralPanel" class="generalPanel panel">
+							<div id="scheduleBasicInfo">
+								<h4>Basic Scheduling Information</h4>
+								<dl>
+								<dt>Schedule Time</dt>
+									<dd>
+										<input id="advHour" type="text" size="2" value="12"/>
+										<input id="advMinutes" type="text" size="2" value="00"/>
+										<select id="advAm_pm">
+											<option>pm</option>
+											<option>am</option>
+										</select>
+										<select id="advTimezone">
+											<option>PDT</option>
+											<option>UTC</option>
+										</select>
+									</dd>
+									<dt>Schedule Date</dt>
+									<dd><input type="text" id="advDate" /></dd>
+									<dt>Recurrence</dt>
+									<dd>
+										<input id="advIs_recurring" type="checkbox" checked  /><span>repeat every</span>
+										<input id="advPeriod" type="text" size="2" value="1"/>
+										<select id="advPeriod_units">
+											<option value="d">Days</option>
+											<option value="h">Hours</option>
+											<option value="m">Minutes</option>
+											<option value="M">Months</option>
+											<option value="w">Weeks</option>
+										</select>
+									</dd>
+								</dl>
+							</div>
+							<div id="scheduleCompleteActions">
+								<h4>Completion Actions</h4>
+								<dl>
+									<dt class="disabled">Failure Action</dt>
+									<dd>
+										<select id="scheduleFailureAction" name="failureAction">
+											<option value="finishCurrent">Finish Current Running</option>
+											<option value="cancelImmediately">Cancel All</option>
+											<option value="finishPossible">Finish All Possible</option>
+										</select>
+									</dd>
+									<dt>Failure Email</dt>
+									<dd>
+										<textarea id="scheduleFailureEmails"></textarea>
+									</dd>
+									<dt>Notify on Failure</dt>
+									<dd>
+										<input id="scheduleNotifyFailureFirst" class="checkbox" type="checkbox" name="notify" value="first" checked >First Failure</input>
+										<input id="scheduleNotifyFailureLast" class="checkbox" type="checkbox" name="notify" value="last">Flow Stop</input>
+									</dd>
+									<dt>Success Email</dt>
+									<dd>
+										<textarea id="scheduleSuccessEmails"></textarea>
+									</dd>
+									<dt class="disabled" >Concurrent Execution</dt>
+									<dd id="scheduleExecutingJob" class="disabled">
+										<input id="scheduleIgnore" class="radio" type="radio" name="concurrent" value="ignore" checked /><label class="radioLabel" for="ignore">Run Concurrently</label>
+										<input id="schedulePipeline" class="radio" type="radio" name="concurrent" value="pipeline" /><label class="radioLabel" for="pipeline">Pipeline</label>
+										<input id="scheduleQueue" class="radio" type="radio" name="concurrent" value="queue" /><label class="radioLabel" for="queue">Queue Job</label>
+									</dd>
+								</dl>
+							</div>
+							<div id="scheduleFlowPropertyOverride">
+								<h4>Flow Property Override</h4>
+								<div class="tableDiv">
+									<table>
+										<thead>
+											<tr>
+												<th>Name</th>
+												<th>Value</th>
+											</tr>
+										</thead>
+										<tbody>
+											<tr id="scheduleAddRow"><td id="scheduleAddRow-col" colspan="2"><span class="addIcon"></span><a href="#">Add Row</a></td></tr>
+										</tbody>
+									</table>
+								</div>
+							</div>
+						</div>
+						<div id="scheduleGraphPanel" class="graphPanel panel">
+							<div id="scheduleJobListCustom" class="jobList">
+								<div class="filterList">
+									<input class="filter" placeholder="  Job Filter" />
+								</div>
+								<div class="list">
+								</div>
+								<div class="btn5 resetPanZoomBtn" >Reset Pan Zoom</div>
+							</div>
+						    <div id="scheduleSvgDivCustom" class="svgDiv" >
+						    	<svg class="svgGraph" xmlns="http://www.w3.org/2000/svg" version="1.1" shape-rendering="optimize-speed" text-rendering="optimize-speed" >
+								</svg>
+							</div>
+						</div>
+					</div>
+						<div class="actions">
+							<a class="yes btn1" id="adv-schedule-btn" href="#">Schedule</a>
+							<a class="no simplemodal-close btn3" id="schedule-cancel-btn" href="#">Cancel</a>
+					</div>
+					</div>
+				</div>
+		
+				<div id="schedule-flow" class="modal">
                         <h3>Schedule Flow</h3>
                         <div id="errorMsg" class="box-error-message">$errorMsg</div>
 
@@ -177,6 +336,7 @@
                         <div class="actions">
                                 <a class="yes btn2" id="schedule-btn" href="#">Schedule The Flow</a>
                                 <a class="no simplemodal-close btn3" href="#">Cancel</a>
+                                <a class="btn2" id="adv-schedule-opt-btn" href="#">Advanced Schedule Options</a>
                         </div>
                 </div>
                 <div id="invalid-session" class="modal">
diff --git a/src/java/azkaban/webapp/servlet/velocity/scheduledflowpage.vm b/src/java/azkaban/webapp/servlet/velocity/scheduledflowpage.vm
index 09d387a..b2511ed 100644
--- a/src/java/azkaban/webapp/servlet/velocity/scheduledflowpage.vm
+++ b/src/java/azkaban/webapp/servlet/velocity/scheduledflowpage.vm
@@ -18,8 +18,12 @@
 <html>
 	<head>
 #parse( "azkaban/webapp/servlet/velocity/style.vm" )
-		<script type="text/javascript" src="${context}/js/jquery/jquery.js"></script>    
-		<script type="text/javascript" src="${context}/js/jqueryui/jquery-ui.custom.min.js"></script>
+		<link rel="stylesheet" type="text/css" href="${context}/css/jquery-ui-timepicker-addon.css" />
+		<link rel="stylesheet" type="text/css" href="${context}/css/jquery-ui.css" />
+		<script type="text/javascript" src="${context}/js/jquery/jquery-1.8.3.min.js"></script>  
+		<script type="text/javascript" src="${context}/js/jqueryui/jquery-ui-1.9.2.custom.min.js"></script>  
+		<script type="text/javascript" src="${context}/js/jqueryui/jquery-ui-timepicker-addon.js"></script> 
+		<script type="text/javascript" src="${context}/js/jqueryui/jquery-ui-sliderAccess.js"></script>
 		<script type="text/javascript" src="${context}/js/namespace.js"></script>
 		<script type="text/javascript" src="${context}/js/underscore-1.2.1-min.js"></script>
 		<script type="text/javascript" src="${context}/js/backbone-0.5.3-min.js"></script>
@@ -32,7 +36,6 @@
 			var timezone = "${timezone}";
 			var errorMessage = null;
 			var successMessage = null;
-	
 		</script>
 	</head>
 	<body>
@@ -43,12 +46,12 @@
 		<div class="content">
 		
 #if($errorMsg)
-                                <div class="box-error-message">$errorMsg</div>
+		<div class="box-error-message">$errorMsg</div>
 #else
 #if($error_message != "null")
-                                <div class="box-error-message">$error_message</div>
+		<div class="box-error-message">$error_message</div>
 #elseif($success_message != "null")
-                                <div class="box-success-message">$success_message</div>
+		<div class="box-success-message">$success_message</div>
 #end
 #end		
 		
@@ -70,7 +73,7 @@
 							<th class="date">First Scheduled to Run</th>
 							<th class="date">Next Execution Time</th>
 							<th class="date">Repeats Every</th>
-							<th class="action">Action</th>
+							<th colspan="2" class="action">Action</th>
 						</tr>
 					</thead>
 					<tbody>
@@ -90,6 +93,7 @@
 							<td>$utils.formatDateTime(${sched.nextExecTime})</td>
 							<td>$utils.formatPeriod(${sched.period})</td>
 							<td><button id="removeSchedBtn" onclick="removeSched(${sched.projectId}, '${sched.flowName}')" >Remove Schedule</button></td>
+							<td><button id="addSlaBtn" onclick="slaView.initFromSched(${sched.projectId}, '${sched.flowName}')" >Set SLA</button></td>
 						</tr>
 #end
 #else
@@ -97,8 +101,117 @@
 #end
 					</tbody>
 				</table>
+			</div>
+
+		
+			<div id="all-sla-content">
+				<div class="section-hd">
+					<h2>Flow SLAs</h2>
+				</div>
+			</div>
+			
+			<div class="scheduledFlows">
+				<table id="slaTbl">
+					<thead>
+						<tr>
+							<th>Flow</th>
+							<th>Project</th>
+							<th>User</th>
+							<th>Submitted By</th>
+							<th class="date">First SLA check Time</th>
+							<th class="date">Next SLA check Time</th>
+							<th class="date">Repeats Every</th>
+							<th colspan="2" class="action">Action</th>
+						</tr>
+					</thead>
+					<tbody>
+						#if($slas)
+#foreach($sla in $slas)
+						<tr class="row" >
+
+							<td class="tb-name">
+								<a href="${context}/manager?project=${sla.projectName}&flow=${sla.flowName}">${sla.flowName}</a>
+							</td>
+							<td>
+								<a href="${context}/manager?project=${sla.projectName}">${sla.projectName}</a>
+							</td>
+							<td>${sla.submitUser}</td>
+							<td>${sla.submitUser}</td>
+							<td>$utils.formatDateTime(${sla.firstCheckTime})</td>
+							<td>$utils.formatDateTime(${sla.nextCheckTime})</td>
+							<td>$utils.formatPeriod(${sla.period})</td>
+							<td><button id="removeSchedBtn" onclick="removeSla(${sla.projectId}, '${sla.flowName}')" >Remove SLA</button></td>
+						</tr>
+#end
+#else
+						<tr><td class="last">No Flow SLA Set</td></tr>
+#end
+					</tbody>
+				</table>
+			</div>
+		</div>
 				
-				
+		<!-- modal content -->
+
+		<div id="slaModalBackground" class="modalBackground2">
+			<div id="sla-options" class="modal modalContainer2">
+				<a href='#' title='Close' class='modal-close'>x</a>
+					<h3>SLA Options</h3>
+					<div>
+						<ul class="optionsPicker">
+							<li id="slaOptions">General SLA Options</li>
+						</ul>
+					</div>
+					<div class="optionsPane">
+						<div id="slaPanel" class="generalPanel panel">
+							<div id="slaActions">
+								<h4>SLA Alert Emails</h4>
+								<dl>
+									<dt >SLA Alert Emails</dt>
+									<dd>
+										<textarea id="slaEmails"></textarea>
+									</dd>
+								</dl>
+							</div>
+							<div id="slaRules">
+								<h4>Flow SLA Rules</h4>
+								<div class="tableDiv">
+									<table id="flowRulesTbl">
+										<thead>
+											<tr>
+												<th>Flow/Job</th>
+												<th>Finish In</th>
+												<th>Email Action</th>
+												<th>Kill Action</th>
+											</tr>
+										</thead>
+										<tbody>
+										</tbody>
+									</table>
+								</div>
+								<h4>Job SLA Rules</h4>
+								<div class="tableDiv">
+									<table id="jobRulesTbl">
+										<thead>
+											<tr>
+												<th>Flow/Job</th>
+												<th>Finish In</th>
+												<th>Email Action</th>
+												<th>Kill Action</th>
+											</tr>
+										</thead>
+										<tbody>
+										</tbody>
+									</table>
+								</div>
+							</div>
+						</div>
+					</div>
+					<div class="actions">
+						<a class="yes btn1" id="remove-sla-btn" href="#">Remove SLA</a>
+						<a class="yes btn1" id="set-sla-btn" href="#">Set SLA</a>
+						<a class="no simplemodal-close btn3" id="sla-cancel-btn" href="#">Cancel</a>
+					</div>
 			</div>
 		</div>
 	</body>
diff --git a/src/sql/create_schedule_table.sql b/src/sql/create_schedule_table.sql
index defadce..381ce6d 100644
--- a/src/sql/create_schedule_table.sql
+++ b/src/sql/create_schedule_table.sql
@@ -12,6 +12,8 @@ CREATE TABLE schedules (
 	next_exec_time BIGINT,
 	submit_time BIGINT,
 	submit_user VARCHAR(128),
+	enc_type TINYINT,
+    options LONGBLOB,
 	primary key(project_id, flow_name)
 ) ENGINE=InnoDB;
 
diff --git a/src/web/css/azkaban.css b/src/web/css/azkaban.css
index 7c1aa97..a3c402c 100644
--- a/src/web/css/azkaban.css
+++ b/src/web/css/azkaban.css
@@ -1351,6 +1351,10 @@ tr:hover td {
 	background-color: #F0F0F0;
 }
 
+.radioLabel.disabled {
+	opacity: 0.3;
+}
+
 #executing-options {
 	left: 100px;
 	right: 100px;
@@ -1358,14 +1362,6 @@ tr:hover td {
 	bottom: 40px;
 }
 
-#scheduled {
-	
-}
-
-.radioLabel.disabled {
-	opacity: 0.3;
-}
-
 #executing-options .svgDiv {
 	position: absolute;
 	background-color: #CCC;
@@ -2330,6 +2326,326 @@ span .nowrap {
 	cursor: pointer;
 }
 
+#schedule-options {
+	left: 100px;
+	right: 100px;
+	top: 50px;
+	bottom: 40px;
+}
+
+#schedule-options .svgDiv {
+	position: absolute;
+	background-color: #CCC;
+	padding: 1px;
+	left: 270px;
+	right: 0px;
+	top: 0px;
+	bottom: 0px;
+}
+
+#schedule-options .jobList {
+	position: absolute;
+	width: 255px;
+	top: 0px;
+	bottom: 0px;
+	padding: 5px;
+	background-color: #F0F0F0;
+}
+
+#schedule-options .list {
+	width: 255px;
+}
+
+#schedule-options ul.optionsPicker {
+	margin-left: 30px;
+}
+
+#schedule-options ul.optionsPicker li {
+	float: left;
+	font-size: 12pt;
+	font-weight: bold;
+	margin-right: 15px;
+	cursor: pointer;
+	color: #CCC;
+}
+
+#schedule-options ul.optionsPicker li.selected {
+	text-decoration: underline;
+	color: #000;
+}
+
+#schedule-options ul.optionsPicker li.selected:hover {
+	color: #000;
+}
+
+#schedule-options ul.optionsPicker li:hover {
+	color: #888;
+}
+
+#schedule-options .optionsPane {
+	position: absolute;
+	top: 85px;
+	background-color: #FFF;
+	left: 0px;
+	right: 0px;
+	bottom: 0px;
+}
+
+#schedule-options .panel {
+	position: absolute;
+	width: 100%;
+	top: 0px;
+	bottom: 65px;
+}
+
+#schedule-options .generalPanel.panel {
+	background-color: #F4F4F4;
+	padding-top: 15px;
+}
+
+#schedule-options h3 {
+	margin-left: 20px;
+	font-size: 14pt;
+	border-bottom: 1px solid #CCC;
+}
+
+#schedule-options h4 {
+	margin-left: 20px;
+	font-size: 12pt;
+	border-bottom: 1px solid #CCC;
+}
+
+#scheduleGeneralPanel {
+	overflow: auto;
+}
+
+#scheduleGeneralPanel dt {
+	width: 150px;
+	font-size: 10pt;
+	font-weight: bold;
+	margin-top: 5px;
+}
+
+#scheduleGeneralPanel textarea {
+	width: 500px;
+}
+
+#scheduleGeneralPanel table #addRow {
+	cursor: pointer;
+}
+
+#scheduleGeneralPanel table tr {
+	height: 24px;
+}
+
+#scheduleGeneralPanel table .editable {
+
+}
+
+#scheduleGeneralPanel table .editable input {
+	border: 1px solid #009FC9;
+	height: 16px;
+}
+
+#scheduleGeneralPanel table .name {
+	width: 40%;
+}
+
+#scheduleGeneralPanel span.addIcon {
+	display: block;
+	width: 16px;
+	height: 16px;
+	background-image: url("./images/addIcon.png");
+}
+
+#scheduleGeneralPanel span.removeIcon {
+	display: block;
+	visibility:hidden;
+	disabled: true;
+	width: 16px;
+	height: 16px;
+	background-image: url("./images/removeIcon.png");
+	cursor: pointer;
+}
+
+#scheduleGeneralPanel .editable:hover span.removeIcon {
+	visibility:visible;
+}
+
+#scheduleGeneralPanel {
+}
+
+#scheduleGeneralPanel span {
+	float: left;
+	margin-left: 5px;
+}
+
+#scheduleGeneralPanel dd {
+	font-size: 10pt;
+}
+
+
+
+
+#sla-options {
+	left: 100px;
+	right: 100px;
+	top: 50px;
+	bottom: 40px;
+}
+
+#sla-options .svgDiv {
+	position: absolute;
+	background-color: #CCC;
+	padding: 1px;
+	left: 270px;
+	right: 0px;
+	top: 0px;
+	bottom: 0px;
+}
+
+#sla-options .jobList {
+	position: absolute;
+	width: 255px;
+	top: 0px;
+	bottom: 0px;
+	padding: 5px;
+	background-color: #F0F0F0;
+}
+
+#sla-options .list {
+	width: 255px;
+}
+
+#sla-options ul.optionsPicker {
+	margin-left: 30px;
+}
+
+#sla-options ul.optionsPicker li {
+	float: left;
+	font-size: 12pt;
+	font-weight: bold;
+	margin-right: 15px;
+	cursor: pointer;
+	color: #CCC;
+}
+
+#sla-options ul.optionsPicker li.selected {
+	text-decoration: underline;
+	color: #000;
+}
+
+#sla-options ul.optionsPicker li.selected:hover {
+	color: #000;
+}
+
+#sla-options ul.optionsPicker li:hover {
+	color: #888;
+}
+
+#sla-options .optionsPane {
+	position: absolute;
+	top: 85px;
+	background-color: #FFF;
+	left: 0px;
+	right: 0px;
+	bottom: 0px;
+}
+
+#sla-options .panel {
+	position: absolute;
+	width: 100%;
+	top: 0px;
+	bottom: 65px;
+}
+
+#sla-options .generalPanel.panel {
+	background-color: #F4F4F4;
+	padding-top: 15px;
+}
+
+#sla-options h3 {
+	margin-left: 20px;
+	font-size: 14pt;
+	border-bottom: 1px solid #CCC;
+}
+
+#sla-options h4 {
+	margin-left: 20px;
+	font-size: 12pt;
+	border-bottom: 1px solid #CCC;
+}
+
+#slaPanel {
+	overflow: auto;
+}
+
+#slaPanel dt {
+	width: 150px;
+	font-size: 10pt;
+	font-weight: bold;
+	margin-top: 5px;
+}
+
+#slaPanel textarea {
+	width: 500px;
+}
+
+#slaPanel table #addRow {
+	cursor: pointer;
+}
+
+#slaPanel table tr {
+	height: 24px;
+}
+
+#slaPanel table .editable {
+
+}
+
+#slaPanel table .editable input {
+	border: 1px solid #009FC9;
+	height: 16px;
+}
+
+#slaPanel table .name {
+	width: 40%;
+}
+
+#slaPanel span.addIcon {
+	display: block;
+	width: 16px;
+	height: 16px;
+	background-image: url("./images/addIcon.png");
+}
+
+#slaPanel span.removeIcon {
+	display: block;
+	visibility:hidden;
+	disabled: true;
+	width: 16px;
+	height: 16px;
+	background-image: url("./images/removeIcon.png");
+	cursor: pointer;
+}
+
+#slaPanel .editable:hover span.removeIcon {
+	visibility:visible;
+}
+
+#slaPanel {
+}
+
+#slaPanel span {
+	float: left;
+	margin-left: 5px;
+}
+
+#slaPanel dd {
+	font-size: 10pt;
+}
+
+
 .azkaban-charts .expandable-hitarea { background-position: -32px -16px; }
 .azkaban-charts .expandable-hitarea.collapse { background-position: 0 -16px; }
 /* clean up */
diff --git a/src/web/js/azkaban.exflow.options.view.js b/src/web/js/azkaban.exflow.options.view.js
index e6fdefb..035e18d 100644
--- a/src/web/js/azkaban.exflow.options.view.js
+++ b/src/web/js/azkaban.exflow.options.view.js
@@ -15,9 +15,11 @@
  */
 
 var executeFlowView;
+var advancedScheduleView;
 var customSvgGraphView;
 var customJobListView;
 var cloneModel;
+var scheduleModel;
 
 function recurseAllAncestors(nodes, disabledMap, id, disable) {
 	var node = nodes[id];
@@ -86,6 +88,425 @@ azkaban.ContextMenu = Backbone.View.extend({
 	}
 });
 
+azkaban.AdvancedScheduleView = Backbone.View.extend({
+	  events : {
+	  	"click" : "closeEditingTarget",
+	    "click #adv-schedule-btn": "handleScheduleFlow",
+	    "click #schedule-cancel-btn": "handleCancel",
+	    "click .modal-close": "handleCancel",
+	    "click #scheduleGeneralOptions": "handleGeneralOptionsSelect",
+	    "click #scheduleFlowOptions": "handleFlowOptionsSelect",
+	    "click #scheduleSlaOptions": "handleSlaOptionsSelect",
+	    "click #scheduleAddRow": "handleAddRow",
+	    "click table .editable": "handleEditColumn",
+	    "click table .removeIcon": "handleRemoveColumn"
+	  },
+	  initialize: function(setting) {
+	  	 this.contextMenu = new azkaban.ContextMenu({el:$('#disableJobMenu')});
+	  	 this.handleGeneralOptionsSelect();
+	  },
+	  show: function() {
+	  	this.handleGeneralOptionsSelect();
+	  	$('#advScheduleModalBackground').show();
+	  	$('#schedule-options').show();
+	  	this.cloneModel = this.model.clone();
+	  	scheduleModel = this.cloneModel;
+	  	
+	  	var fetchData = {"project": projectName, "ajax":"flowInfo", "flow":flowName};
+//	  	if (execId) {
+//	  		fetchData.execid = execId;
+//	  	}
+	  	this.executeURL = contextURL + "/executor";
+	  	this.scheduleURL = contextURL + "/schedule";
+	  	var handleAddRow = this.handleAddRow;
+	  	
+	  	var data = this.cloneModel.get("data");
+	  	var nodes = {};
+	  	for (var i=0; i < data.nodes.length; ++i) {
+	      	var node = data.nodes[i];
+	      	nodes[node.id] = node;
+	    }
+    	
+    	for (var i=0; i < data.edges.length; ++i) {
+    	  	var edge = data.edges[i];
+    	  	var fromNode = nodes[edge.from];
+    	  	var toNode = nodes[edge.target];
+    	  	
+    	  	if (!fromNode.outNodes) {
+    	  		fromNode.outNodes = {};
+    	  	}
+    	  	fromNode.outNodes[toNode.id] = toNode;
+    	  	
+    	  	if (!toNode.inNodes) {
+    	  		toNode.inNodes = {};
+    	  	}
+    	  	toNode.inNodes[fromNode.id] = fromNode;
+    	}
+	    this.cloneModel.set({nodes: nodes});
+	  	
+	  	var disabled = {};
+//		for (var i = 0; i < data.nodes.length; ++i) {
+//			var updateNode = data.nodes[i];
+//			if (updateNode.status == "DISABLED" || updateNode.status == "SKIPPED") {
+//				updateNode.status = "READY";
+//				disabled[updateNode.id] = true;
+//			}
+//			if (updateNode.status == "SUCCEEDED") {
+//				disabled[updateNode.id] = true;
+//			}
+//		}
+	  	this.cloneModel.set({disabled: disabled});
+	  	
+	  	$.get(
+			this.executeURL,
+			fetchData,
+			function(data) {
+				if (data.error) {
+					alert(data.error);
+				}
+				else {
+					if (data.successEmails) {
+						$('#scheduleSuccessEmails').val(data.successEmails.join());
+					}
+					if (data.failureEmails) {
+						$('#scheduleFailureEmails').val(data.failureEmails.join());
+					}
+					
+					if (data.failureAction) {
+						$('#scheduleFailureAction').val(data.failureAction);
+					}
+					if (data.notifyFailureFirst) {
+						$('#scheduleNotifyFailureFirst').attr('checked', true);
+					}
+					if (data.notifyFailureLast) {
+						$('#scheduleNotifyFailureLast').attr('checked', true);	
+					}
+					if (data.flowParam) {
+						var flowParam = data.flowParam;
+						for (var key in flowParam) {
+							var row = handleAddRow();
+							var td = $(row).find('td');
+							$(td[0]).text(key);
+							$(td[1]).text(flowParam[key]);
+						}
+					}
+
+					if (!data.running || data.running.length == 0) {
+						$(".radio").attr("disabled", "disabled");
+						$(".radioLabel").addClass("disabled", "disabled");
+					}
+				}
+			},
+			"json"
+		);
+	  },
+	  handleCancel: function(evt) {
+	  	var scheduleURL = contextURL + "/schedule";
+		$('#advScheduleModalBackground').hide();
+	  	$('#schedule-options').hide();
+	  },
+	  handleGeneralOptionsSelect: function(evt) {
+	  	$('#scheduleFlowOptions').removeClass('selected');
+	  	$('#scheduleSlaOptions').removeClass('selected');
+	  	$('#scheduleGeneralOptions').addClass('selected');
+
+	  	$('#scheduleGeneralPanel').show();	  	
+	  	$('#scheduleGraphPanel').hide();
+	  	$('#scheduleSlaPanel').hide();
+	  },
+	  handleSlaOptionsSelect: function(evt) {
+		  	$('#scheduleFlowOptions').removeClass('selected');
+		  	$('#scheduleSlaOptions').addClass('selected');
+		  	$('#scheduleGeneralOptions').removeClass('selected');
+		  	
+		  	$('#scheduleSlaPanel').show();	  	
+		  	$('#scheduleGraphPanel').hide();
+		  	$('#scheduleGeneralPanel').hide();
+		  },
+	  handleFlowOptionsSelect: function(evt) {
+	  	$('#scheduleGeneralOptions').removeClass('selected');
+	  	$('#scheduleFlowOptions').addClass('selected');
+	  	$('#scheduleSlaOptions').removeClass('selected');
+	  	
+	  	$('#scheduleGraphPanel').show();	  	
+	  	$('#scheduleGeneralPanel').hide();
+	  	$('#scheduleSlaPanel').hide();
+	  	
+	  	if (this.flowSetup) {
+	  		return;
+	  	}
+	  	
+	  	customSvgGraphView = new azkaban.SvgGraphView({el:$('#scheduleSvgDivCustom'), model: this.cloneModel, rightClick: {id: 'disableJobMenu', callback: this.handleDisableMenuClick}});
+		customJobsListView = new azkaban.JobListView({el:$('#scheduleJobListCustom'), model: this.cloneModel, rightClick: {id: 'disableJobMenu', callback: this.handleDisableMenuClick}});
+		this.cloneModel.trigger("change:graph");
+		
+		this.flowSetup = true;
+	  },
+	  handleScheduleFlow: function(evt) {
+	  	var scheduleURL = contextURL + "/scheduler";
+	  	var disabled = this.cloneModel.get("disabled");
+	  	var failureAction = $('#failureAction').val();
+	  	var failureEmails = $('#failureEmails').val();
+	  	var successEmails = $('#successEmails').val();
+	  	var notifyFailureFirst = $('#notifyFailureFirst').is(':checked');
+	  	var notifyFailureLast = $('#notifyFailureLast').is(':checked');
+	  	var executingJobOption = $('input:radio[name=gender]:checked').val();
+	  	
+	  	var flowOverride = {};
+	  	var editRows = $(".editRow");
+		for (var i = 0; i < editRows.length; ++i) {
+			var row = editRows[i];
+			var td = $(row).find('td');
+			var key = $(td[0]).text();
+			var val = $(td[1]).text();
+			
+			if (key && key.length > 0) {
+				flowOverride[key] = val;
+			}
+		}
+
+	  	var scheduleData = {
+	  		project: projectName,
+	  		ajax: "advScheduleFlow",
+	  		flow: flowName,
+	  		hour: $('#advHour').val(),
+	  		min: $('#advMinutes').val(),
+	  		am_pm: $('#advAm_pm').val(),
+	  		timezone: $('#advTimezone').val(),
+	  		date: $('#advDate').val(),
+	  		period: $('#advPeriod').val()+$('#advPeriod_units').val(),
+	  		disable: this.cloneModel.get('disabled'),
+	  		failureAction: failureAction,
+	  		failureEmails: failureEmails,
+	  		successEmails: successEmails,
+	  		notifyFailureFirst: notifyFailureFirst,
+	  		notifyFailureLast: notifyFailureLast,
+	  		executingJobOption: executingJobOption,
+	  		flowOverride: flowOverride
+	  	};
+	  	
+		$.get(
+			scheduleURL,
+			scheduleData,
+			function(data) {
+				if (data.error) {
+					alert(data.error);
+				}
+				else {
+					var redirectURL = contextURL + "/schedule";
+					window.location.href = redirectURL;
+				}
+			},
+			"json"
+		);
+	  },
+	  handleAddRow: function(evt) {
+	  	var tr = document.createElement("tr");
+	  	var tdName = document.createElement("td");
+	    var tdValue = document.createElement("td");
+	    
+	    var icon = document.createElement("span");
+	    $(icon).addClass("removeIcon");
+	    var nameData = document.createElement("span");
+	    $(nameData).addClass("spanValue");
+	    var valueData = document.createElement("span");
+	    $(valueData).addClass("spanValue");
+	    	    
+		$(tdName).append(icon);
+		$(tdName).append(nameData);
+		$(tdName).addClass("name");
+		$(tdName).addClass("editable");
+		
+		$(tdValue).append(valueData);
+	    $(tdValue).addClass("editable");
+		
+		$(tr).addClass("editRow");
+	  	$(tr).append(tdName);
+	  	$(tr).append(tdValue);
+	   
+	  	$(tr).insertBefore("#scheduleAddRow");
+	  	return tr;
+	  },
+	  handleEditColumn : function(evt) {
+	  	var curTarget = evt.currentTarget;
+	
+	  	if (this.editingTarget != curTarget) {
+			this.closeEditingTarget();
+			
+			var text = $(curTarget).children(".spanValue").text();
+			$(curTarget).empty();
+						
+			var input = document.createElement("input");
+			$(input).attr("type", "text");
+			$(input).css("width", "100%");
+			$(input).val(text);
+			$(curTarget).addClass("editing");
+			$(curTarget).append(input);
+			$(input).focus();
+			this.editingTarget = curTarget;
+	  	}
+	  },
+	  handleRemoveColumn : function(evt) {
+	  	var curTarget = evt.currentTarget;
+	  	// Should be the table
+	  	var row = curTarget.parentElement.parentElement;
+		$(row).remove();
+	  },
+	  closeEditingTarget: function(evt) {
+	  	if (this.editingTarget != null && this.editingTarget != evt.target && this.editingTarget != evt.target.parentElement ) {
+	  		var input = $(this.editingTarget).children("input")[0];
+	  		var text = $(input).val();
+	  		$(input).remove();
+
+		    var valueData = document.createElement("span");
+		    $(valueData).addClass("spanValue");
+		    $(valueData).text(text);
+
+	  		if ($(this.editingTarget).hasClass("name")) {
+		  		var icon = document.createElement("span");
+		    	$(icon).addClass("removeIcon");
+		    	$(this.editingTarget).append(icon);
+		    }
+		    
+		    $(this.editingTarget).removeClass("editing");
+		    $(this.editingTarget).append(valueData);
+		    this.editingTarget = null;
+	  	}
+	  },
+	  handleDisableMenuClick : function(action, el, pos) {
+			var jobid = el[0].jobid;
+			var requestURL = contextURL + "/manager?project=" + projectName + "&flow=" + flowName + "&job=" + jobid;
+			if (action == "open") {
+				window.location.href = requestURL;
+			}
+			else if(action == "openwindow") {
+				window.open(requestURL);
+			}
+			else if(action == "disable") {
+				var disabled = scheduleModel.get("disabled");
+		
+				disabled[jobid] = true;
+				scheduleModel.set({disabled: disabled});
+				scheduleModel.trigger("change:disabled");
+			}
+			else if(action == "disableAll") {
+				var disabled = scheduleModel.get("disabled");
+		
+				var nodes = scheduleModel.get("nodes");
+				for (var key in nodes) {
+					disabled[key] = true;
+				}
+
+				scheduleModel.set({disabled: disabled});
+				scheduleModel.trigger("change:disabled");
+			}
+			else if (action == "disableParents") {
+				var disabled = scheduleModel.get("disabled");
+				var nodes = scheduleModel.get("nodes");
+				var inNodes = nodes[jobid].inNodes;
+		
+				if (inNodes) {
+					for (var key in inNodes) {
+					  disabled[key] = true;
+					}
+				}
+				
+				scheduleModel.set({disabled: disabled});
+				scheduleModel.trigger("change:disabled");
+			}
+			else if (action == "disableChildren") {
+				var disabledMap = scheduleModel.get("disabled");
+				var nodes = scheduleModel.get("nodes");
+				var outNodes = nodes[jobid].outNodes;
+		
+				if (outNodes) {
+					for (var key in outNodes) {
+					  disabledMap[key] = true;
+					}
+				}
+				
+				scheduleModel.set({disabled: disabledMap});
+				scheduleModel.trigger("change:disabled");
+			}
+			else if (action == "disableAncestors") {
+				var disabled = scheduleModel.get("disabled");
+				var nodes = scheduleModel.get("nodes");
+				
+				recurseAllAncestors(nodes, disabled, jobid, true);
+				
+				scheduleModel.set({disabled: disabled});
+				scheduleModel.trigger("change:disabled");
+			}
+			else if (action == "disableDescendents") {
+				var disabled = scheduleModel.get("disabled");
+				var nodes = scheduleModel.get("nodes");
+				
+				recurseAllDescendents(nodes, disabled, jobid, true);
+				
+				scheduleModel.set({disabled: disabled});
+				scheduleModel.trigger("change:disabled");
+			}
+			else if(action == "enable") {
+				var disabled = scheduleModel.get("disabled");
+		
+				disabled[jobid] = false;
+				scheduleModel.set({disabled: disabled});
+				scheduleModel.trigger("change:disabled");
+			}
+			else if(action == "enableAll") {
+				disabled = {};
+				scheduleModel.set({disabled: disabled});
+				scheduleModel.trigger("change:disabled");
+			}
+			else if (action == "enableParents") {
+				var disabled = scheduleModel.get("disabled");
+				var nodes = scheduleModel.get("nodes");
+				var inNodes = nodes[jobid].inNodes;
+		
+				if (inNodes) {
+					for (var key in inNodes) {
+					  disabled[key] = false;
+					}
+				}
+				
+				scheduleModel.set({disabled: disabled});
+				scheduleModel.trigger("change:disabled");
+			}
+			else if (action == "enableChildren") {
+				var disabled = scheduleModel.get("disabled");
+				var nodes = scheduleModel.get("nodes");
+				var outNodes = nodes[jobid].outNodes;
+		
+				if (outNodes) {
+					for (var key in outNodes) {
+					  disabled[key] = false;
+					}
+				}
+				
+				scheduleModel.set({disabled: disabled});
+				scheduleModel.trigger("change:disabled");
+			}
+			else if (action == "enableAncestors") {
+				var disabled = scheduleModel.get("disabled");
+				var nodes = scheduleModel.get("nodes");
+				
+				recurseAllAncestors(nodes, disabled, jobid, false);
+				
+				this.cloneModel.set({disabled: disabled});
+				this.cloneModel.trigger("change:disabled");
+			}
+			else if (action == "enableDescendents") {
+				var disabled = this.cloneModel.get("disabled");
+				var nodes = this.cloneModel.get("nodes");
+				
+				recurseAllDescendents(nodes, disabled, jobid, false);
+				
+				scheduleModel.set({disabled: disabled});
+				scheduleModel.trigger("change:disabled");
+			}
+		}
+});
+
 azkaban.ExecuteFlowView = Backbone.View.extend({
   	  events : {
   	  	"click" : "closeEditingTarget",
diff --git a/src/web/js/azkaban.flow.view.js b/src/web/js/azkaban.flow.view.js
index 0624033..7c05e77 100644
--- a/src/web/js/azkaban.flow.view.js
+++ b/src/web/js/azkaban.flow.view.js
@@ -282,77 +282,83 @@ azkaban.ExecutionModel = Backbone.Model.extend({});
 
 var scheduleFlowView;
 azkaban.ScheduleFlowView = Backbone.View.extend({
-  events : {
-    "click #schedule-btn": "handleScheduleFlow"
-  },
-  initialize : function(settings) {
-  	$( "#datepicker" ).datepicker();
-  	$( "#datepicker" ).datepicker('setDate', new Date());
-    $("#errorMsg").hide();
-  },
-  handleScheduleFlow : function(evt) {
-         // First make sure we can upload
-//     var projectName = $('#path').val();
-     var description = $('#description').val();
-
-     var hourVal = $('#hour').val();
-     var minutesVal = $('#minutes').val();
-     var ampmVal = $('#am_pm').val();
-     var timezoneVal = $('#timezone').val();
-     var dateVal = $('#datepicker').val();
-     var is_recurringVal = $('#is_recurring').val();
-     var periodVal = $('#period').val();
-     var periodUnits = $('#period_units').val();
+	events : {
+	"click #schedule-btn": "handleScheduleFlow",
+	"click #adv-schedule-opt-btn": "handleAdvancedSchedule"
+	},
+	initialize : function(settings) {
+		$( "#datepicker" ).datepicker();
+		$( "#datepicker" ).datepicker('setDate', new Date());
+		$("#errorMsg").hide();
+	},
+	handleAdvancedSchedule : function(evt) {
+		console.log("Clicked advanced schedule options button");
+		//$('#confirm-container').hide();
+		$.modal.close();
+		advancedScheduleView.show();
+	},
+	show: function() {
+//		this.cloneModel = this.model.clone();
+//		cloneModel = this.cloneModel;
+	},
+	handleScheduleFlow : function(evt) {
 
-     console.log("Creating schedule for "+projectName+"."+flowName);
-     $.ajax({
-        async: "false",
-        url: "schedule",
-        dataType: "json",
-        type: "POST",
-        data: {
-		action:"scheduleFlow", 
+	var hourVal = $('#hour').val();
+	var minutesVal = $('#minutes').val();
+	var ampmVal = $('#am_pm').val();
+	var timezoneVal = $('#timezone').val();
+	var dateVal = $('#datepicker').val();
+	var is_recurringVal = $('#is_recurring').val();
+	var periodVal = $('#period').val();
+	var periodUnits = $('#period_units').val();
 
-		projectId:projectId,
-		projectName:projectName, 
-		flowName:flowName,
-		hour:hourVal,
-		minutes:minutesVal,
-		am_pm:ampmVal,
-		timezone:timezoneVal,
-		date:dateVal,
-		userExec:"dummy",
-		is_recurring:is_recurringVal,
-		period:periodVal,
-		period_units:periodUnits
-		},
-        success: function(data) {
-                if (data.status == "success") {
-			console.log("Successfully scheduled for "+projectName+"."+flowName);
-                        if (data.action == "redirect") {
-                                window.location = contextURL + "/manager?project=" + projectName + "&flow=" + flowName ;
-                        }
-			else{
-				$("#success_message").text("Flow " + projectName + "." + flowName + " scheduled!" );			
- 				window.location = contextURL + "/manager?project=" + projectName + "&flow=" + flowName ; 
+	console.log("Creating schedule for "+projectName+"."+flowName);
+	$.ajax({
+		async: "false",
+		url: "schedule",
+		dataType: "json",
+		type: "POST",
+		data: {
+			action:"scheduleFlow", 
+			projectId:projectId,
+			projectName:projectName, 
+			flowName:flowName,
+			hour:hourVal,
+			minutes:minutesVal,
+			am_pm:ampmVal,
+			timezone:timezoneVal,
+			date:dateVal,
+			userExec:"dummy",
+			is_recurring:is_recurringVal,
+			period:periodVal,
+			period_units:periodUnits
+			},
+		success: function(data) {
+			if (data.status == "success") {
+				console.log("Successfully scheduled for "+projectName+"."+flowName);
+				if (data.action == "redirect") {
+					window.location = contextURL + "/manager?project=" + projectName + "&flow=" + flowName ;
+				}
+				else{
+					$("#success_message").text("Flow " + projectName + "." + flowName + " scheduled!" );			
+	 				window.location = contextURL + "/manager?project=" + projectName + "&flow=" + flowName ; 
+				}
+			}
+			else {
+				if (data.action == "login") {
+					window.location = "";
+				}
+				else {
+					$("#errorMsg").text("ERROR: " + data.message);
+					$("#errorMsg").slideDown("fast");
+				}
 			}
-                }
-                else {
-                        if (data.action == "login") {
-                                        window.location = "";
-                        }
-                        else {
-                                $("#errorMsg").text("ERROR: " + data.message);
-                                $("#errorMsg").slideDown("fast");
-                        }
-                }
-        }
-     });
+		}
+	});
 
-  },
-  render: function() {
-	  
-  }
+	},
+	render: function() {
+	}
 });
 
 
@@ -367,8 +373,9 @@ $(function() {
 	graphModel = new azkaban.GraphModel();
 	svgGraphView = new azkaban.SvgGraphView({el:$('#svgDiv'), model: graphModel, rightClick: {id: 'jobMenu', callback: handleJobMenuClick}});
 	jobsListView = new azkaban.JobListView({el:$('#jobList'), model: graphModel, rightClick: {id: 'jobMenu', callback: handleJobMenuClick}});
-	scheduleFlowView = new azkaban.ScheduleFlowView({el:$('#schedule-flow')});
+	scheduleFlowView = new azkaban.ScheduleFlowView({el:$('#schedule-flow'),   model: graphModel});
 	executeFlowView = new azkaban.ExecuteFlowView({el:$('#executing-options'), model: graphModel});
+	advancedScheduleView = new azkaban.AdvancedScheduleView({el:$('#schedule-options'), model: graphModel});
 	var requestURL = contextURL + "/manager";
 
 	// Set up the Flow options view. Create a new one every time :p
@@ -435,20 +442,22 @@ $(function() {
 	      "json"
 	    );
 	    
+	    
 	$('#scheduleflowbtn').click( function() {
-	  console.log("schedule button clicked");
-	  $('#schedule-flow').modal({
-          closeHTML: "<a href='#' title='Close' class='modal-close'>x</a>",
-          position: ["20%",],
-          containerId: 'confirm-container',
-          containerCss: {
-            'height': '220px',
-            'width': '500px'
-          },
-          onShow: function (dialog) {
-            var modal = this;
-            $("#errorMsg").hide();
-          }
-        });
+		console.log("schedule button clicked");
+		$('#schedule-flow').modal({
+			closeHTML: "<a href='#' title='Close' class='modal-close'>x</a>",
+			position: ["20%",],
+			containerId: 'confirm-container',
+			containerCss: {
+			'height': '220px',
+			'width': '500px'
+			},
+			onShow: function (dialog) {
+				var modal = this;
+				$("#errorMsg").hide();
+			}
+		});
 	});
+	
 });
diff --git a/src/web/js/azkaban.scheduled.view.js b/src/web/js/azkaban.scheduled.view.js
index 5cea0ac..9b0ee9a 100644
--- a/src/web/js/azkaban.scheduled.view.js
+++ b/src/web/js/azkaban.scheduled.view.js
@@ -1,21 +1,276 @@
 $.namespace('azkaban');
 
+
 function removeSched(projectId, flowName) {
-    var scheduleURL = contextURL + "/schedule"
-    var redirectURL = contextURL + "/schedule"
-    $.post(
-         scheduleURL,
-         {"action":"removeSched", "projectId":projectId, "flowName":flowName},
-         function(data) {
-             if (data.error) {
+	var scheduleURL = contextURL + "/schedule"
+	var redirectURL = contextURL + "/schedule"
+	$.post(
+			scheduleURL,
+			{"action":"removeSched", "projectId":projectId, "flowName":flowName},
+			function(data) {
+				if (data.error) {
+//                 alert(data.error)
+					$('#errorMsg').text(data.error)
+				}
+				else {
+// 		 alert("Schedule "+schedId+" removed!")
+					window.location = redirectURL
+				}
+			},
+			"json"
+	)
+}
+
+function removeSla(projectId, flowName) {
+	var scheduleURL = contextURL + "/schedule"
+	var redirectURL = contextURL + "/schedule"
+	$.post(
+			scheduleURL,
+			{"action":"removeSla", "projectId":projectId, "flowName":flowName},
+			function(data) {
+				if (data.error) {
 //                 alert(data.error)
-                 $('#errorMsg').text(data.error)
-             }
-	     else {
+					$('#errorMsg').text(data.error)
+				}
+				else {
 // 		 alert("Schedule "+schedId+" removed!")
-		 window.location = redirectURL
-             }
-         },
-         "json"
-   )
+					window.location = redirectURL
+				}
+			},
+			"json"
+	)
 }
+
+azkaban.ChangeSlaView = Backbone.View.extend({
+	events : {
+		"click" : "closeEditingTarget",
+		"click #set-sla-btn": "handleSetSla",	
+		"click #remove-sla-btn": "handleRemoveSla",
+		"click #sla-cancel-btn": "handleSlaCancel",
+		"click .modal-close": "handleSlaCancel",
+	},
+	initialize: function(setting) {
+
+	},
+	handleSlaCancel: function(evt) {
+		console.log("Clicked cancel button");
+		var scheduleURL = contextURL + "/schedule";
+
+		$('#slaModalBackground').hide();
+		$('#sla-options').hide();
+	},
+	initFromSched: function(projId, flowName) {
+		this.projectId = projId;
+		this.flowName = flowName;
+		this.scheduleURL = contextURL + "/schedule"
+		var fetchScheduleData = {"projId": this.projectId, "ajax":"schedInfo", "flowName":this.flowName};
+		
+		$.get(
+				this.scheduleURL,
+				fetchScheduleData,
+				function(data) {
+					if (data.error) {
+						alert(data.error);
+					}
+					else {
+						if (data.slaEmails) {
+							$('#slaEmails').val(data.slaEmails.join());
+						}
+						var flowRulesTbl = document.getElementById("flowRulesTbl").tBodies[0];
+						var flowRuleRow = flowRulesTbl.insertRow(-1);
+						var cflowName = flowRuleRow.insertCell(0);
+						cflowName.innerHTML = flowName;
+						var cflowduration = flowRuleRow.insertCell(1);
+						var flowDuration = document.createElement("input");
+						flowDuration.setAttribute("type", "text");
+						flowDuration.setAttribute("id", "flowDuration");
+						flowDuration.setAttribute("class", "durationpick");
+						if(data.flowRules) {
+							flowDuration.setAttribute("value", data.flowRules.duration);
+						}
+						cflowduration.appendChild(flowDuration);
+						var emailAct = flowRuleRow.insertCell(2);
+						var checkEmailAct = document.createElement("input");
+						checkEmailAct.setAttribute("type", "checkbox");
+						emailAct.appendChild(checkEmailAct);
+						var killAct = flowRuleRow.insertCell(3);
+						var checkKillAct = document.createElement("input");
+						checkKillAct.setAttribute("type", "checkbox");
+						killAct.appendChild(checkKillAct);
+						
+						var jobRulesTbl = document.getElementById("jobRulesTbl").tBodies[0];
+						var allJobs = data.allJobs;
+						for (var job in allJobs) {
+							
+							var jobRuleRow = jobRulesTbl.insertRow(-1);
+							var cjobName = jobRuleRow.insertCell(0);
+							cjobName.innerHTML = allJobs[job];
+							var cjobduration = jobRuleRow.insertCell(1);
+							var jobDuration = document.createElement("input");
+							jobDuration.setAttribute("type", "text");
+							jobDuration.setAttribute("id", "jobDuration");
+							jobDuration.setAttribute("class", "durationpick");
+							if(data.jobRules) {
+								jobDuration.setAttribute("value", data.jobRules[job].duration);
+							}
+							cjobduration.appendChild(jobDuration);
+							
+							var emailAct = jobRuleRow.insertCell(2);
+							var checkEmailAct = document.createElement("input");
+							checkEmailAct.setAttribute("type", "checkbox");
+							emailAct.appendChild(checkEmailAct);
+							var killAct = jobRuleRow.insertCell(3);
+							var checkKillAct = document.createElement("input");
+							checkKillAct.setAttribute("type", "checkbox");
+							killAct.appendChild(checkKillAct);
+						}
+						$('.durationpick').timepicker({hourMax: 99});
+					}
+				},
+				"json"
+			);
+		
+		$('#slaModalBackground').show();
+		$('#sla-options').show();
+		
+//		this.schedFlowOptions = sched.flowOptions
+		console.log("Loaded schedule info. Ready to set SLA.");
+
+	},
+	handleRemoveSla: function(evt) {
+		console.log("Clicked remove sla button");
+		var scheduleURL = contextURL + "/schedule"
+		var redirectURL = contextURL + "/schedule"
+		$.post(
+				scheduleURL,
+				{"action":"removeSla", "projectId":this.projectId, "flowName":this.flowName},
+				function(data) {
+				if (data.error) {
+						$('#errorMsg').text(data.error)
+					}
+					else {
+						window.location = redirectURL
+					}
+				"json"
+				}
+			);
+
+	},
+	handleSetSla: function(evt) {
+
+		var slaEmails = $('#slaEmails').val();
+
+//		var flowRules = {};
+		var flowRulesTbl = document.getElementById("flowRulesTbl").tBodies[0];
+		var flowRuleRow = flowRulesTbl.rows[0];
+//		flowRules["flowDuration"] = flowRuleRow.cells[1].firstChild.value;
+//		flowRules["flowEmailAction"] = flowRuleRow.cells[2].firstChild.value;
+//		flowRules["flowKillAction"] = flowRuleRow.cells[3].firstChild.value;
+		var flowRules = flowRuleRow.cells[1].firstChild.value + ',' + flowRuleRow.cells[2].firstChild.value + ',' + flowRuleRow.cells[3].firstChild.value;
+		
+		var jobRules = {};
+		var jobRulesTbl = document.getElementById("jobRulesTbl").tBodies[0];
+		console.log(jobRulesTbl.rows.length);
+		for(var row = 0; row < jobRulesTbl.rows.length; row++) {
+			
+			var jobRow = jobRulesTbl.rows[row];
+			var jobRule = {};
+			
+			console.log(row);
+			console.log(jobRow.cells[0].firstChild.value);
+//			jobRule["jobDuration"] = jobRow.cells[1].firstChild.value;
+//			jobRule["jobEmailAction"] = jobRow.cells[2].firstChild.value;
+//			jobRule["jobKillAction"] = jobRow.cells[3].firstChild.value;
+//			jobRules[jobRow.cells[0].innerHTML] = jobRule;
+			jobRules[jobRow.cells[0].innerHTML] = jobRow.cells[1].firstChild.value + ',' + jobRow.cells[2].firstChild.value + ',' +  jobRow.cells[3].firstChild.value;
+		}
+		
+		var slaData = {
+			projectId: this.projectId,
+			flowName: this.flowName,
+			ajax: "setSla",			
+			slaEmails: slaEmails,
+			flowRules: flowRules,
+			jobRules: jobRules
+		};
+
+		$.get(
+			this.scheduleURL,
+			slaData,
+			function(data) {
+				if (data.error) {
+					alert(data.error);
+				}
+				else {
+					window.location.href = this.scheduleURL;
+				}
+			},
+			"json"
+		);
+	},
+	handleEditColumn : function(evt) {
+		var curTarget = evt.currentTarget;
+	
+		if (this.editingTarget != curTarget) {
+			this.closeEditingTarget();
+			
+			var text = $(curTarget).children(".spanValue").text();
+			$(curTarget).empty();
+						
+			var input = document.createElement("input");
+			$(input).attr("type", "text");
+			$(input).css("width", "100%");
+			$(input).val(text);
+			$(curTarget).addClass("editing");
+			$(curTarget).append(input);
+			$(input).focus();
+			this.editingTarget = curTarget;
+		}
+	},
+	handleRemoveColumn : function(evt) {
+		var curTarget = evt.currentTarget;
+		// Should be the table
+		var row = curTarget.parentElement.parentElement;
+		$(row).remove();
+	},
+	closeEditingTarget: function(evt) {
+		if (this.editingTarget != null && this.editingTarget != evt.target && this.editingTarget != evt.target.parentElement ) {
+			var input = $(this.editingTarget).children("input")[0];
+			var text = $(input).val();
+			$(input).remove();
+			
+			var valueData = document.createElement("span");
+			$(valueData).addClass("spanValue");
+			$(valueData).text(text);
+
+			if ($(this.editingTarget).hasClass("name")) {
+				var icon = document.createElement("span");
+				$(icon).addClass("removeIcon");
+				$(this.editingTarget).append(icon);
+			}
+			
+			$(this.editingTarget).removeClass("editing");
+			$(this.editingTarget).append(valueData);
+			this.editingTarget = null;
+		}
+	}
+});
+
+var slaView;
+
+$(function() {
+	var selected;
+
+
+	slaView = new azkaban.ChangeSlaView({el:$('#sla-options')});
+	
+//	var requestURL = contextURL + "/manager";
+
+	// Set up the Flow options view. Create a new one every time :p
+//	 $('#addSlaBtn').click( function() {
+//		 slaView.show();
+//	 });
+
+	 
+	
+});
\ No newline at end of file
diff --git a/unit/java/azkaban/scheduler/JdbcScheduleLoaderTest.java b/unit/java/azkaban/scheduler/JdbcScheduleLoaderTest.java
index 84dd074..43d3067 100644
--- a/unit/java/azkaban/scheduler/JdbcScheduleLoaderTest.java
+++ b/unit/java/azkaban/scheduler/JdbcScheduleLoaderTest.java
@@ -7,6 +7,7 @@ import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 import javax.sql.DataSource;
 
@@ -15,6 +16,7 @@ import junit.framework.Assert;
 import org.apache.commons.dbutils.DbUtils;
 import org.apache.commons.dbutils.QueryRunner;
 import org.apache.commons.dbutils.ResultSetHandler;
+import org.joda.time.DateTimeZone;
 import org.junit.AfterClass;
 import org.junit.BeforeClass;
 import org.junit.Test;
@@ -27,7 +29,7 @@ public class JdbcScheduleLoaderTest {
 	private static boolean testDBExists;
 	private static final String host = "localhost";
 	private static final int port = 3306;
-	private static final String database = "azkaban";
+	private static final String database = "azkaban2";
 	private static final String user = "azkaban";
 	private static final String password = "azkaban";
 	private static final int numConnections = 10;
@@ -95,39 +97,6 @@ public class JdbcScheduleLoaderTest {
 		}
 	}
 	
-//	@Test
-//	public void testLoadSchedule() {
-//		if (!testDBExists) {
-//			return;
-//		}
-//
-//		DataSource dataSource = DataSourceUtils.getMySQLDataSource(host, port, database, user, password, numConnections);
-//		Connection connection = null;
-//		try {
-//			connection = dataSource.getConnection();
-//		} catch (SQLException e) {
-//			e.printStackTrace();
-//			testDBExists = false;
-//			DbUtils.closeQuietly(connection);
-//			return;
-//		}
-//
-////		CountHandler countHandler = new CountHandler();
-//		QueryRunner runner = new QueryRunner();
-//		try {
-//			int count = runner.update(connection, "DELETE FROM schedules");
-//			
-//		} catch (SQLException e) {
-//			e.printStackTrace();
-//			testDBExists = false;
-//			DbUtils.closeQuietly(connection);
-//			return;
-//		}
-//		finally {
-//			DbUtils.closeQuietly(connection);
-//		}
-//	}
-	
 	@Test
 	public void testInsertAndLoadSchedule() throws ScheduleManagerException {
 		if (!isTestSetup()) {
@@ -137,12 +106,26 @@ public class JdbcScheduleLoaderTest {
 		
 		JdbcScheduleLoader loader = createLoader();
 		
-		Schedule s1 = new Schedule(1, "proj1", "flow1", "ready", 11111, "America/Los_Angeles", "1d", 22222, 33333, 44444, "cyu");
-		Schedule s2 = new Schedule(1, "proj1", "flow2", "ready", 11111, "America/Los_Angeles", "1d", 22222, 33333, 44444, "ccc");
-		Schedule s3 = new Schedule(2, "proj1", "flow1", "ready", 11111, "America/Los_Angeles", "1d", 22222, 33333, 44444, "cyu");
-		Schedule s4 = new Schedule(3, "proj2", "flow1", "ready", 11111, "America/Los_Angeles", "1d", 22222, 33333, 44444, "cyu");
-		Schedule s5 = new Schedule(3, "proj2", "flow2", "ready", 11111, "America/Los_Angeles", "1d", 22222, 33333, 44444, "cyu");
-		Schedule s6 = new Schedule(3, "proj2", "flow3", "error", 11111, "America/Los_Angeles", "1d", 22222, 33333, 44444, "cyu");
+		Map<String, Object> scheduleOptions = new HashMap<String, Object>();
+		List<String> disabled = new ArrayList<String>();
+		disabled.add("job1");
+		disabled.add("job2");
+		disabled.add("job3");
+		List<String> failEmails = new ArrayList<String>();
+		failEmails.add("email1");
+		failEmails.add("email2");
+		failEmails.add("email3");
+		boolean hasSla = true;
+		scheduleOptions.put("disabled", disabled);
+		scheduleOptions.put("failEmails", failEmails);
+		scheduleOptions.put("hasSla", hasSla);
+		
+		Schedule s1 = new Schedule(1, "proj1", "flow1", "ready", 11111, "America/Los_Angeles", "1d", 22222, 33333, 44444, "cyu", scheduleOptions);
+		Schedule s2 = new Schedule(1, "proj1", "flow2", "ready", 11111, "America/Los_Angeles", "1d", 22222, 33333, 44444, "ccc", scheduleOptions);
+		Schedule s3 = new Schedule(2, "proj1", "flow1", "ready", 11111, "America/Los_Angeles", "1d", 22222, 33333, 44444, "cyu", scheduleOptions);
+		Schedule s4 = new Schedule(3, "proj2", "flow1", "ready", 11111, "America/Los_Angeles", "1d", 22222, 33333, 44444, "cyu", scheduleOptions);
+		Schedule s5 = new Schedule(3, "proj2", "flow2", "ready", 11111, "America/Los_Angeles", "1d", 22222, 33333, 44444, "cyu", scheduleOptions);
+		Schedule s6 = new Schedule(3, "proj2", "flow3", "error", 11111, "America/Los_Angeles", "1d", 22222, 33333, 44444, "cyu", scheduleOptions);
 		
 		loader.insertSchedule(s1);
 		loader.insertSchedule(s2);
@@ -157,7 +140,53 @@ public class JdbcScheduleLoaderTest {
 		Assert.assertEquals("America/Los_Angeles", schedules.get(0).getTimezone().getID());
 		Assert.assertEquals(44444, schedules.get(0).getSubmitTime());
 		Assert.assertEquals("1d", Schedule.createPeriodString(schedules.get(0).getPeriod()));
+		System.out.println("the options are " + schedules.get(0).getSchedOptions());
+		Assert.assertEquals(true, schedules.get(0).getSchedOptions().get("hasSla"));
+	}
+	
+	@Test
+	public void testInsertAndUpdateSchedule() throws ScheduleManagerException {
+		if (!isTestSetup()) {
+			return;
+		}
+		clearDB();
+		
+		JdbcScheduleLoader loader = createLoader();
 		
+		Map<String, Object> scheduleOptions = new HashMap<String, Object>();
+		List<String> disabled = new ArrayList<String>();
+		disabled.add("job1");
+		disabled.add("job2");
+		disabled.add("job3");
+		List<String> failEmails = new ArrayList<String>();
+		failEmails.add("email1");
+		failEmails.add("email2");
+		failEmails.add("email3");
+		boolean hasSla = true;
+		scheduleOptions.put("disabled", disabled);
+		scheduleOptions.put("failEmails", failEmails);
+		scheduleOptions.put("hasSla", hasSla);
+		
+		System.out.println("the options are " + scheduleOptions);
+		Schedule s1 = new Schedule(1, "proj1", "flow1", "ready", 11111, "America/Los_Angeles", "1d", 22222, 33333, 44444, "cyu", scheduleOptions);
+
+		loader.insertSchedule(s1);
+		
+		hasSla = false;
+		scheduleOptions.put("hasSla", hasSla);
+		
+		Schedule s2 = new Schedule(1, "proj1", "flow1", "ready", 11112, "America/Los_Angeles", "2M", 22223, 33334, 44445, "cyu", scheduleOptions);
+
+		loader.updateSchedule(s2);
+		
+		List<Schedule> schedules = loader.loadSchedules();
+		
+		Assert.assertEquals(1, schedules.size());
+		Assert.assertEquals("America/Los_Angeles", schedules.get(0).getTimezone().getID());
+		Assert.assertEquals(44445, schedules.get(0).getSubmitTime());
+		Assert.assertEquals("2M", Schedule.createPeriodString(schedules.get(0).getPeriod()));
+		System.out.println("the options are " + schedules.get(0).getSchedOptions());
+		Assert.assertEquals(false, schedules.get(0).getSchedOptions().get("hasSla"));
 	}
 	
 	@Test
@@ -172,11 +201,25 @@ public class JdbcScheduleLoaderTest {
 		
 		List<Schedule> schedules = new ArrayList<Schedule>();
 		
-		int stress = 100;
+		int stress = 10;
 		
 		for(int i=0; i<stress; i++)
 		{
-			Schedule s = new Schedule(i+1, "proj"+(i+1), "flow1", "ready", 11111, "America/Los_Angeles", "1d", 22222, 33333, 44444, "cyu");
+			Map<String, Object> scheduleOptions = new HashMap<String, Object>();
+			List<String> disabled = new ArrayList<String>();
+			disabled.add("job1");
+			disabled.add("job2");
+			disabled.add("job3");
+			List<String> failEmails = new ArrayList<String>();
+			failEmails.add("email1");
+			failEmails.add("email2");
+			failEmails.add("email3");
+			boolean hasSla = true;
+			scheduleOptions.put("disabled", disabled);
+			scheduleOptions.put("failEmails", failEmails);
+			scheduleOptions.put("hasSla", hasSla);
+			
+			Schedule s = new Schedule(i+1, "proj"+(i+1), "flow1", "ready", 11111, "America/Los_Angeles", "1d", 22222, 33333, 44444, "cyu", scheduleOptions);
 			schedules.add(s);
 			try {
 				loader.insertSchedule(s);