| @@ -1006,6 +1006,8 @@ public abstract class Broadcast | |||
| */ | |||
| protected void pause(String reason, PrintWriter out) | |||
| { | |||
| if (state == BroadcastState.ACCEPTED || state.isFinal) return; | |||
| // Sets state to PAUSING, which is monitored by Broadcast.Service threads. | |||
| // EVentually, when all service activity ends, the state transitions to PAUSED | |||
| StateChangeResult result = setState(BroadcastState.PAUSING, reason, null); | |||
| @@ -1025,6 +1027,7 @@ public abstract class Broadcast | |||
| protected void resume(String reason, PrintWriter out) | |||
| { | |||
| if (state == BroadcastState.ACCEPTED || state.isFinal) return; | |||
| if (!withinOperatingHours()) | |||
| { | |||
| if (out != null) out.write("Cannot resume outside operating hours"); | |||
| @@ -1450,16 +1453,37 @@ public abstract class Broadcast | |||
| for (String key : new String[] {DAILY_STOP_KEY, DAILY_START_KEY}) { | |||
| String value = (String)configuration.get(key); | |||
| if (value != null) { | |||
| timeChanged = timeChanged || setOperatingHours(key, value); | |||
| if (setOperatingHours(key, value)) { | |||
| timeChanged = true; | |||
| } | |||
| } | |||
| } | |||
| if (timeChanged) enforceOperationHours(); | |||
| } | |||
| /** | |||
| * YML: At this time, we only enforce pause action when a broadcast is | |||
| * outside its operating hours. The current design is not satisfactory and needs | |||
| * a better solution. | |||
| * | |||
| * We are not automatically resuming a paused broadcast because the difference | |||
| * between intention of an operator-initiated pause and | |||
| * that of a clock pause needs clarification in their operation paradigm. | |||
| * Question is when or if we allow a operator-initiated pause be resumed | |||
| * when someone changes the operating hours of a broadcast in such a way | |||
| * that the broadcast is at once within its operasting hours. it may be | |||
| * be counter to the intention of the original operator. | |||
| * | |||
| * On the other hand, if that places the broadcast outside it operating hours, | |||
| * it is safer to immediately pause it. | |||
| * | |||
| * To add clarity, we may need to separate the PAUSE state into OPERATOR_PAUSE and CLOCK_PAUSE, | |||
| * and similarly PAUING state. | |||
| */ | |||
| void enforceOperationHours() { | |||
| if (state == BroadcastState.ABORTED) return; | |||
| if (withinOperatingHours()) { | |||
| resume("clock", null); | |||
| // resume("clock", null); | |||
| } else { | |||
| pause("clock", null); | |||
| } | |||
| @@ -1476,11 +1500,11 @@ public abstract class Broadcast | |||
| if (timeOfDay == null) throw new RuntimeException(String.format("Invalid value for %s: %s", timeParam, value)); | |||
| switch (timeParam) { | |||
| case DAILY_STOP_KEY: | |||
| if (daily_stop == timeOfDay) return false; | |||
| if (timeOfDay.equals(daily_stop)) return false; | |||
| daily_stop = timeOfDay; | |||
| return true; | |||
| case DAILY_START_KEY: | |||
| if (daily_start == timeOfDay) return false; | |||
| if (timeOfDay.equals(daily_start)) return false; | |||
| daily_start = timeOfDay; | |||
| return true; | |||
| default: | |||
| @@ -5,6 +5,8 @@ import java.io.FileInputStream; | |||
| import java.io.FileNotFoundException; | |||
| import java.io.IOException; | |||
| import java.io.PrintWriter; | |||
| import java.time.LocalTime; | |||
| import java.time.format.DateTimeFormatter; | |||
| import java.util.Enumeration; | |||
| import java.util.HashMap; | |||
| import java.util.Iterator; | |||
| @@ -25,6 +27,7 @@ import org.apache.log4j.Logger; | |||
| import org.apache.log4j.PropertyConfigurator; | |||
| import org.json.simple.JSONObject; | |||
| import org.json.simple.parser.JSONParser; | |||
| import org.json.simple.parser.ParseException; | |||
| import altk.comm.engine.Broadcast.BroadcastState; | |||
| @@ -92,13 +95,25 @@ public abstract class CommEngine extends HttpServlet | |||
| { | |||
| while (!threadShouldStop) | |||
| { | |||
| String timeOfDay = LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm")); | |||
| // Check for pause | |||
| for (Broadcast broadcast : broadcasts.values()) | |||
| { | |||
| broadcast.enforceOperationHours(); | |||
| {; | |||
| // Check for pause | |||
| if (broadcast.daily_stop.length() > 0) | |||
| { | |||
| if (timeOfDay.equals(broadcast.daily_stop)) broadcast.pause("clock", null); | |||
| } | |||
| // Check for resume | |||
| if (broadcast.daily_start.length() > 0) | |||
| { | |||
| if (timeOfDay.equals(broadcast.daily_start)) broadcast.resume("clock", null); | |||
| } | |||
| } | |||
| long currentTime = System.currentTimeMillis(); | |||
| long sleepTime = 60000 - currentTime % 60000; | |||
| long sleepTime = 60000 + 30000 - currentTime % 60000; | |||
| if (sleepTime > 0) | |||
| { | |||
| try | |||
| @@ -508,13 +523,13 @@ public abstract class CommEngine extends HttpServlet | |||
| protected static String checkTimeOfDay(String timeOfDay) { | |||
| timeOfDay = timeOfDay.trim(); | |||
| // pattern hh:mm | |||
| Pattern pattern = Pattern.compile("^(\\d\\d):[0-5]\\d$"); | |||
| Pattern pattern = Pattern.compile("^(\\d+):([0-5]\\d)$"); | |||
| Matcher matcher = pattern.matcher(timeOfDay); | |||
| if (!matcher.find()) return null; | |||
| // Check hour in range | |||
| String hh = matcher.group(1); | |||
| String hh = matcher.group(1).trim(); | |||
| if (Integer.parseInt(hh) > 23) return null; | |||
| return timeOfDay; | |||
| return (hh.length()==1? "0" + hh : hh) + ":" + matcher.group(2); | |||
| } | |||
| /** | |||
| @@ -532,12 +547,15 @@ public abstract class CommEngine extends HttpServlet | |||
| JSONObject configuration = (JSONObject) parser.parse(jsonString); | |||
| configure(configuration); | |||
| } catch (Exception e) { | |||
| myLogger.error(e); | |||
| out.write("Error - " + e.getMessage()); | |||
| String errMsg = | |||
| (e instanceof ParseException)? ("JSON error: " + e) : e.getMessage(); | |||
| myLogger.error(errMsg, e); | |||
| out.write("Error - " + errMsg); | |||
| // restore current confiuration | |||
| try { | |||
| configure(origConfigJSON); | |||
| } catch (Exception e1) { | |||
| myLogger.error("Internal error in restoring original configuration: " + e1.getMessage(), e1); | |||
| out.write("\nInternal error in restoring original configuration: " + e1.getMessage()); | |||
| } | |||
| } | |||