Imported from dev1.link2tek.net CommEngine.git
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

834 line
26 KiB

  1. package altk.comm.engine;
  2. import java.io.File;
  3. import java.io.FileInputStream;
  4. import java.io.FileNotFoundException;
  5. import java.io.IOException;
  6. import java.io.PrintWriter;
  7. import java.time.LocalTime;
  8. import java.time.format.DateTimeFormatter;
  9. import java.util.Date;
  10. import java.util.Enumeration;
  11. import java.util.HashMap;
  12. import java.util.Iterator;
  13. import java.util.Map;
  14. import java.util.Properties;
  15. import java.util.Vector;
  16. import java.util.concurrent.ScheduledExecutorService;
  17. import java.util.regex.Matcher;
  18. import java.util.regex.Pattern;
  19. import javax.servlet.ServletContext;
  20. import javax.servlet.ServletException;
  21. import javax.servlet.http.HttpServlet;
  22. import javax.servlet.http.HttpServletRequest;
  23. import javax.servlet.http.HttpServletResponse;
  24. import org.apache.log4j.Logger;
  25. import org.apache.log4j.PropertyConfigurator;
  26. import org.json.simple.JSONObject;
  27. import org.json.simple.parser.JSONParser;
  28. import altk.comm.engine.Broadcast.BroadcastState;
  29. @SuppressWarnings("serial")
  30. public abstract class CommEngine extends HttpServlet
  31. {
  32. static final String DAILY_PAUSE_KEY = "daily_pause";
  33. static final String DAILY_RESUME_KEY = "daily_resume";
  34. static final String REQUEST_TOP_ELEMENT_NAME_DEFAULT = "Request";
  35. private static final long DEAD_BROADCAST_VIEWING_PERIOD_DEFAULT = 60;
  36. public static final String SERVICE_THREADPOOL_SIZE_KEY = "service_threadpool_size";
  37. private static final int SERVICE_THREADPOOL_SIZE_DEFAULT = 1;
  38. private static final String POSTBACK_THREADPOOL_SIZE_KEY = "postback_threadpool_size";
  39. private static final int POSTBACK_THREADPOOL_SIZE_DEFAULT = 20;
  40. private static final String POSTBACK_MAX_QUEUE_SIZE_KEY = "postback_max_queue_size";
  41. private static final int POSTBACK_MAX_QUEUE_SIZE_DEFAULT = 10000;
  42. private static final String POSTBACK_MAX_BATCH_SIZE_KEY = "postback_max_batch_size";
  43. private static final int POSTBACK_MAX_BATCH_SIZE_DEFAULT = 100;
  44. private static final String PAUSE_THRESHOLD_KEY = "pause_threshold";
  45. private static final int PAUSE_THRESHOLD_DEFAULT = 0;
  46. /**
  47. * Maps a broadcastId to a broadcast.
  48. */
  49. private Map<String, Broadcast> broadcasts;
  50. protected boolean notInService;
  51. protected Properties config;
  52. protected final String engineName; // e.g. "broadcast_sms", "broadcast_voice"
  53. private long startupTimestamp;
  54. // Sequencing naming of broadcast that fails to yield its broadcastId
  55. private int unknownBroadcastIdNdx = 1;
  56. /**
  57. * Used to communicate media-specific platform resources to broadcasts
  58. */
  59. protected EngineResources resources;
  60. protected static Logger myLogger;
  61. private ScheduledExecutorService scheduler;
  62. private long deadBroadcastViewingMinutes;
  63. private int completedJobCount = 0;
  64. protected String runtimeDirPath;
  65. protected String confDirPath;
  66. /** Daily resume all broadcasts time. No action if "" */
  67. protected String daily_resume = "";
  68. /** Daily pause all broadcasts time. No action if "" */
  69. protected String daily_pause = "";
  70. private DailyClock dailyClock;
  71. protected class DailyClock extends Thread
  72. {
  73. private boolean threadShouldStop = false;
  74. public void run()
  75. {
  76. while (!threadShouldStop)
  77. {
  78. String timeOfDay = LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm"));
  79. // Check for pause
  80. for (Broadcast broadcast : broadcasts.values())
  81. {
  82. // Check for pause
  83. if (broadcast.daily_pause.length() > 0)
  84. {
  85. if (timeOfDay.equals(broadcast.daily_pause)) broadcast.pause("clock", null);
  86. }
  87. else if (timeOfDay.equals(daily_pause)) broadcast.pause("clock", null);
  88. // Check for resume
  89. if (broadcast.daily_resume.length() > 0)
  90. {
  91. if (timeOfDay.equals(broadcast.daily_resume)) broadcast.resume("clock", null);
  92. }
  93. else if (timeOfDay.equals(daily_resume)) broadcast.resume("clock", null);
  94. }
  95. long currentTime = System.currentTimeMillis();
  96. long sleepTime = 60000 + 30000 - currentTime % 60000;
  97. if (sleepTime > 0)
  98. {
  99. try
  100. {
  101. Thread.sleep(sleepTime);
  102. }
  103. catch (Exception e)
  104. {
  105. myLogger.error("DailyClock thread caught: " + e.getMessage(), e);
  106. return;
  107. }
  108. }
  109. }
  110. }
  111. public void terminate()
  112. {
  113. threadShouldStop = true;
  114. }
  115. }
  116. abstract protected Broadcast mkBroadcast();
  117. public CommEngine(String engineName)
  118. {
  119. this.engineName = engineName;
  120. broadcasts = new HashMap<String, Broadcast>();
  121. startupTimestamp = System.currentTimeMillis();
  122. }
  123. /**
  124. * Relocates a filepath relative to runtime directory if filepath is not absolute.
  125. * @param filepath
  126. * @return
  127. */
  128. public String relocateToRuntimeDir(String filepath)
  129. {
  130. if (filepath.startsWith("/")) return filepath; // no change to absolute path
  131. String relocated = filepath;
  132. // The next 2 lines take care of pre-git era meaning convention of filepath in properties.
  133. // Then, the runtime is relative to the current working directory of tomcat.
  134. // Now they are relative to the runtimDirPath obtained from the tomcat tomcat context.
  135. // These 2 lines an be deleted when all CommEngines in production are in the git era.
  136. String unwanted_prefix = engineName + "/";
  137. if (filepath.startsWith(unwanted_prefix)) relocated = filepath.substring(unwanted_prefix.length());
  138. relocated = runtimeDirPath + "/" + relocated;
  139. return relocated;
  140. }
  141. /**
  142. * Invoked by servlet container during initialization of servlet.
  143. * @throws ServletException
  144. */
  145. public final void init() throws ServletException
  146. {
  147. // check init parameters
  148. ServletContext servletContext = getServletContext();
  149. confDirPath = servletContext.getInitParameter(getConfDirContextName());
  150. System.out.println("Config directory is configured to be '" + confDirPath + "'. Make sure it and its content are readable by user 'tomcat'");
  151. runtimeDirPath = servletContext.getInitParameter(getRunTimeDirContextName());
  152. System.out.println("Runtime directory is configured to be '" + runtimeDirPath + "'. Make sure it and its content are readable by user 'tomcat'");
  153. File propertiesFile = new File(confDirPath + "/properties");
  154. // Configure log4j using log4j.properties file, \
  155. // sibling to the engine properties file.
  156. // This change is backward compatible with placing the log4j.properties file in
  157. // the class path.
  158. String log4j_properties = confDirPath + "/log4j.properties";
  159. // Relocate file property offetting it by the runtimeDirPath
  160. try
  161. {
  162. Properties prop = new Properties();
  163. prop.load(new FileInputStream(log4j_properties));
  164. Enumeration<Object> e = prop.keys();
  165. while (e.hasMoreElements())
  166. {
  167. String key = (String)e.nextElement();
  168. if (key.toLowerCase().endsWith(".file"))
  169. {
  170. String filepath = prop.getProperty(key);
  171. String relocate = relocateToRuntimeDir(filepath);
  172. prop.setProperty(key, relocate);
  173. System.out.println(key + "=" + relocate);
  174. }
  175. }
  176. PropertyConfigurator.configure(prop);
  177. }
  178. catch (Exception e)
  179. {
  180. System.out.println("Failed to configure log4: " + e);
  181. // Do nothing, assuming the exception is FileNotFoundException.
  182. // Remaining log4j initialization will look for log4j.properties
  183. // file in the class path.
  184. }
  185. // This activates Logger class instantiation. At this point, if lo4j
  186. // is not yet configured,
  187. // it will look for the log4j.properties file in the class path.
  188. myLogger = Logger.getLogger(CommEngine.class);
  189. myLogger.info("init() invoked");
  190. CommonLogger.startup.info("Using lo4j properites file " + log4j_properties);
  191. CommonLogger.startup.info("Using configuration file " + propertiesFile.getAbsolutePath());
  192. config = new Properties();
  193. try
  194. {
  195. config.load(new FileInputStream(propertiesFile));
  196. }
  197. catch (FileNotFoundException e)
  198. {
  199. CommonLogger.alarm.fatal("Properties file " + propertiesFile.getAbsolutePath() + " not found -- abort");
  200. notInService = true;
  201. return;
  202. }
  203. catch (IOException e)
  204. {
  205. CommonLogger.alarm.fatal("Problem in reading properties file " + propertiesFile.getAbsolutePath() + ": " + e.getMessage());
  206. notInService = true;
  207. return;
  208. }
  209. String timeOfDay, timeOfDayStr, key;
  210. key = DAILY_RESUME_KEY;
  211. timeOfDayStr = config.getProperty(key , "");
  212. timeOfDay = checkTimeOfDay(timeOfDayStr);
  213. if (timeOfDay == null) throw new ServletException(String.format("Invlaid valud for %s: %s", key, timeOfDayStr));
  214. daily_resume = timeOfDay;
  215. key = DAILY_PAUSE_KEY;
  216. timeOfDayStr = config.getProperty(key, "");
  217. timeOfDay = checkTimeOfDay(timeOfDayStr);
  218. if (timeOfDay == null) throw new ServletException(String.format("Invlaid valud for %s: %s", key, timeOfDayStr));
  219. daily_pause = timeOfDay;
  220. Thread dailyClock;
  221. CommonLogger.startup.info(String.format("Dead broadcast viewing period: %d minutes", deadBroadcastViewingMinutes));
  222. CommonLogger.startup.info(String.format("service thread pool size: %d", getServiceThreadPoolSize()));
  223. CommonLogger.activity.info("Postback max queue size = " + getPostbackMaxQueueSize());
  224. CommonLogger.activity.info("Postback threadpool size = " + getPostbackSenderPoolSize());
  225. CommonLogger.activity.info("Postback max batch size = " + getPostbackMaxBatchSize());
  226. CommonLogger.activity.info("daily_resume = " + daily_resume);
  227. CommonLogger.activity.info("daily_pause = " + daily_pause);
  228. dailyClock = new DailyClock();
  229. dailyClock.start();
  230. try
  231. {
  232. // Set up periodic purge of stale broadcasts, based on deadBroadcastViewingMinutes
  233. String periodStr = config.getProperty("dead_broadcast_viewing_period", Long.toString(DEAD_BROADCAST_VIEWING_PERIOD_DEFAULT));
  234. deadBroadcastViewingMinutes = Long.parseLong(periodStr);
  235. initChild();
  236. }
  237. catch (Exception e)
  238. {
  239. throw new ServletException(e.getMessage(), e);
  240. }
  241. }
  242. public int getPauseThreshold()
  243. {
  244. return getPauseThreshold(config);
  245. }
  246. public int getPauseThreshold(Properties properties)
  247. {
  248. String str = properties.
  249. getProperty(PAUSE_THRESHOLD_KEY, String.valueOf(PAUSE_THRESHOLD_DEFAULT));
  250. int pauseThreshold = Integer.valueOf(str);
  251. return pauseThreshold;
  252. }
  253. public int getServiceThreadPoolSize()
  254. {
  255. return getServiceThreadPoolSize(config);
  256. }
  257. public int getServiceThreadPoolSize(Properties properties)
  258. {
  259. String str = properties.
  260. getProperty(SERVICE_THREADPOOL_SIZE_KEY, String.valueOf(SERVICE_THREADPOOL_SIZE_DEFAULT));
  261. int size = Integer.valueOf(str);
  262. return size;
  263. }
  264. public int getPostbackSenderPoolSize()
  265. {
  266. return getPostbackSenderPoolSize(config);
  267. }
  268. public int getPostbackSenderPoolSize(Properties properties)
  269. {
  270. String str = properties.
  271. getProperty(POSTBACK_THREADPOOL_SIZE_KEY, String.valueOf(POSTBACK_THREADPOOL_SIZE_DEFAULT));
  272. int size = Integer.valueOf(str);
  273. return size;
  274. }
  275. public int getPostbackMaxQueueSize()
  276. {
  277. return getPostbackMaxQueueSize(config);
  278. }
  279. public int getPostbackMaxQueueSize(Properties properties)
  280. {
  281. String str = properties.
  282. getProperty(POSTBACK_MAX_QUEUE_SIZE_KEY, String.valueOf(POSTBACK_MAX_QUEUE_SIZE_DEFAULT));
  283. int size = Integer.valueOf(str);
  284. return size;
  285. }
  286. public int getPostbackMaxBatchSize()
  287. {
  288. return getPostbackMaxBatchSize(config);
  289. }
  290. public int getPostbackMaxBatchSize(Properties properties)
  291. {
  292. String str = properties.
  293. getProperty(POSTBACK_MAX_BATCH_SIZE_KEY, String.valueOf(POSTBACK_MAX_BATCH_SIZE_DEFAULT));
  294. int size = Integer.valueOf(str);
  295. return size;
  296. }
  297. protected void purgeStaleBroadcasts()
  298. {
  299. long now = System.currentTimeMillis();
  300. synchronized (broadcasts)
  301. {
  302. Iterator<String> iter = broadcasts.keySet().iterator();
  303. while (iter.hasNext())
  304. {
  305. Broadcast broadcast = broadcasts.get(iter.next());
  306. if (broadcast.getState().isFinal &&
  307. now - broadcast.changeStateTime > deadBroadcastViewingMinutes * 60 * 1000)
  308. {
  309. completedJobCount += broadcast.getCompletedJobCount();
  310. iter.remove();
  311. }
  312. }
  313. }
  314. }
  315. /**
  316. *
  317. * @return name of parameter in Tomcat context file, specifying properties file
  318. * for this SMSEngine.
  319. */
  320. protected abstract String getConfDirContextName();
  321. protected abstract String getRunTimeDirContextName();
  322. @Override
  323. protected void doPost(HttpServletRequest request, HttpServletResponse response)
  324. {
  325. Broadcast broadcast = mkBroadcast();
  326. broadcast.doPost(request, response, this);
  327. }
  328. /**
  329. * Functions covered are
  330. * get=status
  331. * get=cancel_broadcast&broadcast_id=XXX
  332. */
  333. @Override
  334. protected void doGet(HttpServletRequest request, HttpServletResponse response)
  335. {
  336. PrintWriter out;
  337. try
  338. {
  339. out = response.getWriter();
  340. }
  341. catch (IOException e)
  342. {
  343. CommonLogger.alarm.error("Cannot write a reply: " + e);
  344. return;
  345. }
  346. String get = (String)request.getParameter("get");
  347. if (get == null)
  348. {
  349. // Return http status BAD REQUEST
  350. int httpStatus = HttpServletResponse.SC_BAD_REQUEST;
  351. try
  352. {
  353. response.sendError(httpStatus);
  354. }
  355. catch (IOException e)
  356. {
  357. myLogger.warn("Unnable to return HTTP error code " + httpStatus);
  358. }
  359. return;
  360. }
  361. if (get.equalsIgnoreCase("status"))
  362. {
  363. getStatus(request, out);
  364. }
  365. else if (get.equalsIgnoreCase("cancel_broadcast"))
  366. {
  367. cancelBroadcast(request, out);
  368. }
  369. else if (get.equalsIgnoreCase("pause_broadcast"))
  370. {
  371. pauseBroadcast(request, out);
  372. }
  373. else if (get.equalsIgnoreCase("resume_broadcast"))
  374. {
  375. resumeBroadcast(request, out);
  376. }
  377. else if (get.equalsIgnoreCase("configuration"))
  378. {
  379. getConfiguration(request, out);
  380. }
  381. else if (get.equalsIgnoreCase("configure"))
  382. {
  383. configure(request, out);
  384. }
  385. else
  386. {
  387. out.write(get + " not supported");
  388. }
  389. out.close();
  390. }
  391. /**
  392. * Writes configuration in JSON string to out
  393. * @param request
  394. * @param out
  395. */
  396. protected void getConfiguration(HttpServletRequest request, PrintWriter out)
  397. {
  398. JSONObject configuration = getConfigJSON();
  399. out.print(configuration);
  400. }
  401. @SuppressWarnings("unchecked")
  402. private JSONObject getConfigJSON()
  403. {
  404. JSONObject config = new JSONObject();
  405. // engine configuration
  406. JSONObject EngineConfig = new JSONObject();
  407. config.put("engine", EngineConfig);
  408. EngineConfig.put(DAILY_PAUSE_KEY, daily_pause);
  409. EngineConfig.put(DAILY_RESUME_KEY, daily_resume);
  410. // broadcast configuration
  411. JSONObject broadcastsConfig = new JSONObject();
  412. synchronized (broadcasts) {
  413. for (String broadcastId : broadcasts.keySet())
  414. {
  415. Broadcast broadcast = broadcasts.get(broadcastId);
  416. if (broadcast.getState().isFinal) continue;
  417. broadcastsConfig.put(broadcastId, broadcast.getConfigJSON());
  418. }
  419. }
  420. if (broadcastsConfig.size() > 0) config.put("broadcasts", broadcastsConfig);
  421. childAddConfig(config);
  422. return config;
  423. }
  424. /**
  425. * Derived class may add to configMap
  426. * @param configMap
  427. */
  428. protected void childAddConfig(JSONObject config)
  429. {
  430. }
  431. private void cancelBroadcast(HttpServletRequest request, PrintWriter out)
  432. {
  433. // Get broadcastId from request
  434. String broadcastId = getBroadcastId(request);
  435. Broadcast broadcast = broadcasts.get(broadcastId);
  436. String reason = request.getParameter("reason");
  437. if (broadcast == null)
  438. {
  439. out.format("Broadcast %s does not exist", broadcastId);
  440. return;
  441. }
  442. broadcast.cancel(reason, out);
  443. }
  444. protected void pauseBroadcast(HttpServletRequest request, PrintWriter out)
  445. {
  446. // Get broadcastId from request
  447. String broadcastId = getBroadcastId(request);
  448. Broadcast broadcast = broadcasts.get(broadcastId);
  449. String reason = request.getParameter("reason");
  450. if (broadcast == null)
  451. {
  452. out.format("Broadcast %s does not exist", broadcastId);
  453. return;
  454. }
  455. broadcast.pause(reason, out);
  456. }
  457. protected void resumeBroadcast(HttpServletRequest request, PrintWriter out)
  458. {
  459. // Get broadcastId from request
  460. String broadcastId = getBroadcastId(request);
  461. Broadcast broadcast = broadcasts.get(broadcastId);
  462. String reason = request.getParameter("reason");
  463. if (broadcast == null)
  464. {
  465. out.format("Broadcast %s does not exist", broadcastId);
  466. return;
  467. }
  468. broadcast.resume(reason, out);
  469. }
  470. /**
  471. * Check if timeOfDay is of the form "HH::mm"
  472. * @param timeOfDay
  473. * @return timeOfDay if valid, otherwise null
  474. */
  475. protected static String checkTimeOfDay(String timeOfDay) {
  476. timeOfDay = timeOfDay.trim();
  477. if (timeOfDay.length() == 0) return timeOfDay;
  478. // pattern hh:mm
  479. Pattern pattern = Pattern.compile("^(\\d\\d):[0-5]\\d$");
  480. Matcher matcher = pattern.matcher(timeOfDay);
  481. if (!matcher.find()) return null;
  482. // Check hour in range
  483. String hh = matcher.group(1);
  484. if (Integer.parseInt(hh) > 23) return null;
  485. return timeOfDay;
  486. }
  487. /**
  488. * Writes error message to out. Otherwise writes nothing to out.
  489. * @param request
  490. * @param out
  491. */
  492. protected void configure(HttpServletRequest request, PrintWriter out) {
  493. // save original configuration for roll back in case of error
  494. JSONObject origConfigJSON = getConfigJSON();
  495. String jsonString = request.getParameter("data");
  496. try {
  497. JSONParser parser = new JSONParser();
  498. JSONObject configuration = (JSONObject) parser.parse(jsonString);
  499. configure(configuration);
  500. } catch (Exception e) {
  501. myLogger.error(e);
  502. out.write("Error - " + e.getMessage());
  503. // restore current confiuration
  504. try {
  505. configure(origConfigJSON);
  506. } catch (Exception e1) {
  507. out.write("\nInternal error in restoring original configuration: " + e1.getMessage());
  508. }
  509. }
  510. }
  511. private void configureEngine(JSONObject configuration) throws Exception {
  512. String value, timeOfDay, key;
  513. key = DAILY_PAUSE_KEY;
  514. value = (String)configuration.get(key);
  515. if (value != null) {
  516. timeOfDay = checkTimeOfDay(value);
  517. if (timeOfDay == null) throw new Exception(String.format("Invalid value for %s: %s", key, value));
  518. daily_pause = timeOfDay;
  519. }
  520. key = DAILY_RESUME_KEY;
  521. value = (String)configuration.get(key);
  522. if (value != null) {
  523. timeOfDay = checkTimeOfDay(value);
  524. if (timeOfDay == null) throw new Exception(String.format("Invalid value for %s: %s", key, value));
  525. daily_resume = timeOfDay;
  526. }
  527. }
  528. private void configure(JSONObject configuration) throws Exception {
  529. // emgine
  530. JSONObject engineConfig = (JSONObject)configuration.get("engine");
  531. configureEngine(engineConfig);
  532. // broadcasts
  533. JSONObject broadcastsConfig = (JSONObject)configuration.get("broadcasts");
  534. if (broadcastsConfig != null) {
  535. for (Object broadcastId : broadcastsConfig.keySet())
  536. {
  537. JSONObject broadcastConfig = (JSONObject)broadcastsConfig.get(broadcastId);
  538. Broadcast broadcast = broadcasts.get(broadcastId);
  539. if (broadcast == null) continue;
  540. broadcast.configure(broadcastConfig);
  541. }
  542. }
  543. // derived class
  544. configureChild(configuration);
  545. }
  546. /**
  547. * Derived class updates itself from given configuration.
  548. * @param configuration
  549. */
  550. protected void configureChild(JSONObject configuration)
  551. {
  552. }
  553. /**
  554. * <CallEngine_status>
  555. * status of each broadcast
  556. * <calls><total>ttt</total><connected>nnn</connected>
  557. * </CallEngine_status>
  558. */
  559. private void getStatus(HttpServletRequest request, PrintWriter out)
  560. {
  561. purgeStaleBroadcasts();
  562. String broadcastId = request.getParameter("broadcast_id");
  563. if (broadcastId != null)
  564. {
  565. broadcastId = broadcastId.trim();
  566. if (broadcastId.length() == 0)
  567. {
  568. out.write("broadcast_id request parameter cannot be empty");
  569. return;
  570. }
  571. Broadcast broadcast = broadcasts.get(broadcastId);
  572. if (broadcast == null)
  573. {
  574. out.write("<error>No such broadcast</error>");
  575. }
  576. else
  577. {
  578. out.write(broadcast.mkStatusReport());
  579. }
  580. return;
  581. }
  582. else
  583. {
  584. String tag = engineName + "_status";
  585. out.write("<" + tag + ">\r\n");
  586. out.write("<startup_time>" + startupTimestamp
  587. + "</startup_time>\r\n");
  588. // First get a copy of broadcasts, to avoid mutex deadlock.
  589. Vector<Broadcast> broadcastList = new Vector<Broadcast>();
  590. synchronized(broadcasts)
  591. {
  592. for (String key : broadcasts.keySet())
  593. {
  594. broadcastList.add(broadcasts.get(key));
  595. }
  596. }
  597. // We have released the lock.
  598. // Then append status of each broadcast to outBuf.
  599. for (Broadcast broadcast : broadcastList)
  600. {
  601. out.write(broadcast.mkStatusReport() + "\n");
  602. }
  603. out.write("<job_summary completed='" + getCompletedJobCount() + "' pending='" + getPendingJobCount() + "' active='" + getActiveJobCount() + "'/>\n");
  604. out.write("<engine_clock><daily_pause>" + daily_pause + "</daily_pause><daily_resume>" + daily_resume + "</daily_resume></engine_clock>\n");
  605. out.write("</" + tag + ">");
  606. }
  607. }
  608. public int getPendingJobCount()
  609. {
  610. int readyCount = 0;
  611. synchronized(broadcasts)
  612. {
  613. for (Broadcast broadcast : broadcasts.values())
  614. {
  615. readyCount += broadcast.getPendingJobCount();
  616. }
  617. }
  618. return readyCount;
  619. }
  620. public int getActiveJobCount()
  621. {
  622. int activeCount = 0;
  623. synchronized(broadcasts)
  624. {
  625. for (Broadcast broadcast : broadcasts.values())
  626. {
  627. activeCount += broadcast.getActiveJobCount();
  628. }
  629. }
  630. return activeCount;
  631. }
  632. public int getCompletedJobCount()
  633. {
  634. int additionalCompletedJobCount = 0;
  635. synchronized(broadcasts)
  636. {
  637. for (Broadcast broadcast : broadcasts.values())
  638. {
  639. additionalCompletedJobCount += broadcast.getCompletedJobCount();
  640. }
  641. }
  642. return completedJobCount + additionalCompletedJobCount;
  643. }
  644. public void removeBroadcast(String broadcastId)
  645. {
  646. CommonLogger.activity.info("Removing broadcast " + broadcastId);
  647. synchronized(broadcasts)
  648. {
  649. broadcasts.remove(broadcastId);
  650. }
  651. }
  652. public boolean notInService()
  653. {
  654. return notInService;
  655. }
  656. /**
  657. * Decode http GET request for broadcast_id value
  658. * @param request
  659. * @return broadcast_id
  660. */
  661. private String getBroadcastId(HttpServletRequest request)
  662. {
  663. return request.getParameter("broadcast_id");
  664. }
  665. /**
  666. * Invoked by servlet container when servlet is destroyed.
  667. */
  668. public final void destroy()
  669. {
  670. System.out.println("Destroying " + engineName);
  671. // Shutdown threads that periodically purge stale broadcasts.
  672. scheduler.shutdownNow();
  673. synchronized(broadcasts)
  674. {
  675. for (Broadcast broadcast : broadcasts.values())
  676. {
  677. broadcast.terminate(BroadcastState.ABORTED, "Platform termination");
  678. }
  679. }
  680. // Destroy dailyClock thread
  681. try
  682. {
  683. dailyClock.terminate();
  684. dailyClock.join();
  685. }
  686. catch (InterruptedException e)
  687. {
  688. // TODO nothing
  689. }
  690. destroyChild();
  691. super.destroy();
  692. }
  693. /**
  694. * Indirectly invoked by servlet container during servlet initialization.
  695. */
  696. abstract protected void initChild();
  697. /**
  698. * Indirectly invoked by servlet container during destruction of servlet.
  699. */
  700. abstract protected void destroyChild();
  701. public EngineResources getResources()
  702. {
  703. return resources;
  704. }
  705. public void addBroadcast(String broadcastId, Broadcast broadcast)
  706. {
  707. if (broadcastId == null) broadcastId = "Unknown" + unknownBroadcastIdNdx++;
  708. synchronized (broadcasts)
  709. {
  710. broadcasts.put(broadcastId, broadcast);
  711. }
  712. }
  713. /**
  714. * If broadcast has no id, one will be created for it.
  715. * @param broadcast
  716. */
  717. public void installBroadcast(Broadcast broadcast)
  718. {
  719. String broadcastId = broadcast.getBroadcastId();
  720. if (broadcastId == null) broadcastId = "Unknown" + unknownBroadcastIdNdx++;
  721. synchronized (broadcasts)
  722. {
  723. broadcasts.put(broadcastId, broadcast);
  724. }
  725. }
  726. }