|
|
|
@@ -2,6 +2,7 @@ package altk.comm.engine; |
|
|
|
|
|
|
|
import java.io.IOException; |
|
|
|
import java.io.PrintWriter; |
|
|
|
import java.time.LocalTime; |
|
|
|
import java.util.ArrayList; |
|
|
|
import java.util.Arrays; |
|
|
|
import java.util.HashMap; |
|
|
|
@@ -39,6 +40,8 @@ public abstract class Broadcast |
|
|
|
private static final int SCHEDULER_THREAD_POOL_SIZE = 5; |
|
|
|
private static final String ACTIVITY_RECORD_ID_PARAM_NAME_DEFAULT = "activity_record_id"; |
|
|
|
private static final long SLEEP_BETWEEN_JOBS_DEFAULT = 0; |
|
|
|
static final String DAILY_STOP_KEY = "daily_stop"; |
|
|
|
static final String DAILY_START_KEY = "daily_start"; |
|
|
|
|
|
|
|
public final String broadcastType; |
|
|
|
private String broadcastId; |
|
|
|
@@ -65,8 +68,8 @@ public abstract class Broadcast |
|
|
|
protected String postbackURL; |
|
|
|
private Postback postback; |
|
|
|
public long expireTime; |
|
|
|
protected String daily_resume = ""; |
|
|
|
protected String daily_pause = ""; |
|
|
|
protected String daily_start = ""; |
|
|
|
protected String daily_stop = ""; |
|
|
|
|
|
|
|
/** |
|
|
|
* Sleep time in milliseconds between consecutive job processing (actualliy batch) |
|
|
|
@@ -478,11 +481,15 @@ public abstract class Broadcast |
|
|
|
pauseThreshold = commEngine.getPauseThreshold(); |
|
|
|
try |
|
|
|
{ |
|
|
|
// Check validity of operating hours parameters |
|
|
|
|
|
|
|
boolean notInService = commEngine.notInService(); |
|
|
|
decode(request, notInService); |
|
|
|
// Now that have decoded the id of this broadcast, |
|
|
|
// ask CommEngine to install it with its id. |
|
|
|
commEngine.installBroadcast(this); |
|
|
|
setOperatingHours(DAILY_START_KEY, daily_start); |
|
|
|
setOperatingHours(DAILY_STOP_KEY, daily_stop); |
|
|
|
|
|
|
|
if (notInService) |
|
|
|
{ |
|
|
|
@@ -891,8 +898,8 @@ public abstract class Broadcast |
|
|
|
"' ready='" + getPendingJobCount() + "'"); |
|
|
|
statusBf.append(" active='" + getActiveJobCount() + "'"); |
|
|
|
statusBf.append("></job_summary>\n"); |
|
|
|
statusBf.append("<daily_pause>" + daily_pause + "</daily_pause>"); |
|
|
|
statusBf.append("<daily_resume>" + daily_resume + "</daily_resume>\n"); |
|
|
|
statusBf.append("<daily_stop>" + daily_stop + "</daily_stop>"); |
|
|
|
statusBf.append("<daily_start>" + daily_start + "</daily_start>\n"); |
|
|
|
statusBf.append(additionalStatusXML()); |
|
|
|
statusBf.append("</" + topLevelTag + ">"); |
|
|
|
String statusReport = statusBf.toString(); |
|
|
|
@@ -1018,6 +1025,11 @@ public abstract class Broadcast |
|
|
|
|
|
|
|
protected void resume(String reason, PrintWriter out) |
|
|
|
{ |
|
|
|
if (!withinOperatingHours()) |
|
|
|
{ |
|
|
|
if (out != null) out.write("Cannot resume outside operating hours"); |
|
|
|
return; |
|
|
|
} |
|
|
|
synchronized (resumeFlag) |
|
|
|
{ |
|
|
|
StateChangeResult result = setState(BroadcastState.RUNNING, reason, null); |
|
|
|
@@ -1064,7 +1076,14 @@ public abstract class Broadcast |
|
|
|
changeStateTime = System.currentTimeMillis(); |
|
|
|
if (!serviceThreadsShouldStop()) |
|
|
|
{ |
|
|
|
setState(BroadcastState.RUNNING); |
|
|
|
if (withinOperatingHours()) |
|
|
|
{ |
|
|
|
setState(BroadcastState.RUNNING); |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
setState(BroadcastState.PAUSED, "clock", null); |
|
|
|
} |
|
|
|
|
|
|
|
// Start the dispatcher threads |
|
|
|
for (Service thread : serviceThreadPool) |
|
|
|
@@ -1088,6 +1107,25 @@ public abstract class Broadcast |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private boolean withinOperatingHours() { |
|
|
|
int dailyStartMin = convert2Min(daily_start); |
|
|
|
int dailyStopMin = convert2Min(daily_stop); |
|
|
|
// Ensure daily stop > daily start |
|
|
|
if (dailyStopMin < dailyStartMin) dailyStopMin += 24 * 60; |
|
|
|
LocalTime now = LocalTime.now(); |
|
|
|
int nowMin = now.getHour() * 60 + now.getMinute(); |
|
|
|
if (nowMin < dailyStartMin) nowMin += 24 * 60; |
|
|
|
boolean within = nowMin >= dailyStartMin && nowMin < dailyStopMin; |
|
|
|
return within; |
|
|
|
} |
|
|
|
|
|
|
|
private int convert2Min(String hhmm) { |
|
|
|
String[] parts = hhmm.split(":"); |
|
|
|
int hh = Integer.parseInt(parts[0]); |
|
|
|
int mm = Integer.parseInt(parts[1]); |
|
|
|
return hh * 60 + mm; |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Derived class should wait for end of service before returning. |
|
|
|
* At this point all service threads have already ended. If the derived |
|
|
|
@@ -1401,35 +1439,60 @@ public abstract class Broadcast |
|
|
|
{ |
|
|
|
StringBuffer configBuf = new StringBuffer(); |
|
|
|
configBuf.append("<broadcast_configuration broadcast_id='" + broadcastId + "'>"); |
|
|
|
configBuf.append("<" + CommEngine.DAILY_PAUSE_KEY + ">" + daily_pause + "</" + CommEngine.DAILY_PAUSE_KEY + ">"); |
|
|
|
configBuf.append("<" + CommEngine.DAILY_RESUME_KEY + ">" + daily_resume + "</" + CommEngine.DAILY_RESUME_KEY + ">"); |
|
|
|
configBuf.append("<" + DAILY_STOP_KEY + ">" + daily_stop + "</" + DAILY_STOP_KEY + ">"); |
|
|
|
configBuf.append("<" + DAILY_START_KEY + ">" + daily_start + "</" + DAILY_START_KEY + ">"); |
|
|
|
configBuf.append("</broadcast_configuration>"); |
|
|
|
return configBuf.toString(); |
|
|
|
} |
|
|
|
|
|
|
|
public void configure(JSONObject configuration) throws Exception { |
|
|
|
String value, timeOfDay, key; |
|
|
|
key = CommEngine.DAILY_PAUSE_KEY; |
|
|
|
value = (String)configuration.get(key); |
|
|
|
if (value != null) { |
|
|
|
timeOfDay = CommEngine.checkTimeOfDay(value); |
|
|
|
if (timeOfDay == null) throw new Exception(String.format("Invalid value for %s: %s", key, value)); |
|
|
|
daily_pause = timeOfDay; |
|
|
|
boolean timeChanged = false; |
|
|
|
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); |
|
|
|
} |
|
|
|
} |
|
|
|
key = CommEngine.DAILY_RESUME_KEY; |
|
|
|
value = (String)configuration.get(key); |
|
|
|
if (value != null) { |
|
|
|
timeOfDay = CommEngine.checkTimeOfDay(value); |
|
|
|
if (timeOfDay == null) throw new Exception(String.format("Invalid value for %s: %s", key, value)); |
|
|
|
daily_resume = timeOfDay; |
|
|
|
if (timeChanged) enforceOperationHours(); |
|
|
|
} |
|
|
|
|
|
|
|
void enforceOperationHours() { |
|
|
|
if (state == BroadcastState.ABORTED) return; |
|
|
|
if (withinOperatingHours()) { |
|
|
|
resume("clock", null); |
|
|
|
} else { |
|
|
|
pause("clock", null); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Sets timeParam to value |
|
|
|
* @param timeParam |
|
|
|
* @param value |
|
|
|
* @return false if no change |
|
|
|
*/ |
|
|
|
private boolean setOperatingHours(String timeParam, String value) { |
|
|
|
String timeOfDay = CommEngine.checkTimeOfDay(value); |
|
|
|
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; |
|
|
|
daily_stop = timeOfDay; |
|
|
|
return true; |
|
|
|
case DAILY_START_KEY: |
|
|
|
if (daily_start == timeOfDay) return false; |
|
|
|
daily_start = timeOfDay; |
|
|
|
return true; |
|
|
|
default: |
|
|
|
throw new RuntimeException("Unknown parameter name: " + timeParam); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
@SuppressWarnings("unchecked") |
|
|
|
public JSONObject getConfigJSON() { |
|
|
|
JSONObject dataMap = new JSONObject(); |
|
|
|
dataMap.put(CommEngine.DAILY_PAUSE_KEY, daily_pause); |
|
|
|
dataMap.put(CommEngine.DAILY_RESUME_KEY, daily_resume); |
|
|
|
dataMap.put(DAILY_START_KEY, daily_start); |
|
|
|
dataMap.put(DAILY_STOP_KEY, daily_stop); |
|
|
|
childAddConfigJSON(dataMap); |
|
|
|
return dataMap; |
|
|
|
} |
|
|
|
|