Imported from dev1.link2tek.net CommEngine.git
Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.

496 wiersze
16 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.util.HashMap;
  8. import java.util.Map;
  9. import java.util.Properties;
  10. import java.util.Vector;
  11. import java.util.concurrent.Executors;
  12. import java.util.concurrent.ScheduledExecutorService;
  13. import java.util.concurrent.TimeUnit;
  14. import javax.servlet.ServletContext;
  15. import javax.servlet.http.HttpServlet;
  16. import javax.servlet.http.HttpServletRequest;
  17. import javax.servlet.http.HttpServletResponse;
  18. import org.apache.log4j.Logger;
  19. import altk.comm.engine.Broadcast.BroadcastState;
  20. import altk.comm.engine.exception.BroadcastError;
  21. import altk.comm.engine.exception.BroadcastException;
  22. import altk.comm.engine.exception.PlatformError;
  23. import altk.comm.engine.exception.PlatformException;
  24. import altk.comm.engine.postback.PostBack;
  25. public abstract class CommEngine extends HttpServlet
  26. {
  27. /**
  28. *
  29. */
  30. private static final long serialVersionUID = 6887055442875818654L;
  31. static final String REQUEST_TOP_ELEMENT_NAME_DEFAULT = "Request";
  32. private static final int SCHEDULER_THREAD_POOL_SIZE = 1;
  33. private static final long DEAD_BROADCAST_VIEWING_PERIOD_DEFAULT = 60;
  34. /**
  35. * Maps a broadcastId to a broadcast.
  36. */
  37. private Map<String, Broadcast> broadcasts;
  38. protected boolean notInService;
  39. protected Properties config;
  40. protected Map<String, PostBack> postBackMap;
  41. protected final String engineName; // e.g. "broadcast_sms", "broadcast_voice"
  42. private long startupTimestamp;
  43. // Sequencing naming of broadcast that fails to yield its broadcastId
  44. private int unknownBroadcastIdNdx = 1;
  45. private BroadcastException myException;
  46. /**
  47. * Used to communicate media-specific platform resources to broadcasts
  48. */
  49. protected EngineResources resources;
  50. private static Logger myLogger = Logger.getLogger(CommEngine.class);
  51. private ScheduledExecutorService scheduler;
  52. private long deadBroadcastViewingMinutes;
  53. private int completedJobCount = 0;
  54. abstract protected Broadcast mkBroadcast();
  55. public CommEngine(String engineName)
  56. {
  57. this.engineName = engineName;
  58. broadcasts = new HashMap<String, Broadcast>();
  59. startupTimestamp = System.currentTimeMillis();
  60. myException = null;
  61. }
  62. /**
  63. * Invoked by servlet container during initialization of servlet.
  64. */
  65. public final void init()
  66. {
  67. myLogger.info("init() invoked");
  68. // check init parameters
  69. ServletContext servletContext = getServletContext();
  70. String propertiesFilePath;
  71. propertiesFilePath = servletContext.getInitParameter(getPropertiesContextName());
  72. File propertiesFile = new File(propertiesFilePath);
  73. CommonLogger.startup.info("Using configuration file " + propertiesFile.getAbsolutePath());
  74. config = new Properties();
  75. try
  76. {
  77. config.load(new FileInputStream(propertiesFile));
  78. }
  79. catch (FileNotFoundException e)
  80. {
  81. CommonLogger.alarm.fatal("Properties file " + propertiesFile.getAbsolutePath() + " not found -- abort");
  82. notInService = true;
  83. return;
  84. }
  85. catch (IOException e)
  86. {
  87. CommonLogger.alarm.fatal("Problem in reading properties file " + propertiesFile.getAbsolutePath() + ": " + e.getMessage());
  88. notInService = true;
  89. return;
  90. }
  91. postBackMap = new HashMap<String, PostBack>();
  92. // Set up periodic purge of stale broadcasts, based on deadBroadcastViewingMinutes
  93. String periodStr = config.getProperty("dead_broadcast_viewing_period",
  94. new Long(DEAD_BROADCAST_VIEWING_PERIOD_DEFAULT).toString());
  95. deadBroadcastViewingMinutes = Long.parseLong(periodStr);
  96. CommonLogger.startup.info(String.format("Dead broadcast viewing period: %d minutes", deadBroadcastViewingMinutes));
  97. scheduler = Executors.newScheduledThreadPool(SCHEDULER_THREAD_POOL_SIZE);
  98. scheduler.scheduleAtFixedRate(new Runnable() { public void run() { purgeStaleBroadcasts();}},
  99. deadBroadcastViewingMinutes, deadBroadcastViewingMinutes, TimeUnit.MINUTES);
  100. initChild();
  101. }
  102. protected void purgeStaleBroadcasts()
  103. {
  104. long now = System.currentTimeMillis();
  105. synchronized (broadcasts)
  106. {
  107. for (String id : broadcasts.keySet())
  108. {
  109. if (broadcasts.get(id).changeStateTime - now > deadBroadcastViewingMinutes * 60 * 1000)
  110. {
  111. Broadcast broadcast = broadcasts.get(id);
  112. completedJobCount += broadcast.getCompletedJobCount();
  113. broadcasts.remove(id);
  114. }
  115. }
  116. }
  117. }
  118. /**
  119. *
  120. * @return name of parameter in Tomcat context file, specifying properties file
  121. * for this SMSEngine.
  122. */
  123. protected abstract String getPropertiesContextName();
  124. @Override
  125. protected void doPost(HttpServletRequest request, HttpServletResponse response)
  126. {
  127. try
  128. {
  129. Broadcast broadcast = mkBroadcast();
  130. try
  131. {
  132. broadcast.decode(request, notInService);
  133. if (notInService)
  134. {
  135. throw new PlatformException(PlatformError.RUNTIME_ERROR,
  136. "Not in service");
  137. }
  138. if (broadcast.recipientList.size() == 0)
  139. {
  140. CommonLogger.activity.info("Broadcast " + broadcast.getBroadcastId() + ": No recipients");
  141. broadcast.setState(BroadcastState.COMPLETED, "No recipients", null);
  142. return;
  143. }
  144. // Determine postBackUrl
  145. String postBackURL = broadcast.getPostBackURL();
  146. PostBack postBack = null;
  147. if (postBackURL != null)
  148. {
  149. postBack = postBackMap.get(postBackURL);
  150. if (postBack == null)
  151. {
  152. postBack = new PostBack(postBackURL, broadcast.broadcastType + "_status");
  153. postBackMap.put(postBackURL, postBack);
  154. }
  155. }
  156. broadcast.initSync(resources);
  157. broadcast.init(postBack);
  158. if (broadcast.getState() == BroadcastState.COMPLETED) return;
  159. }
  160. catch (BroadcastException e)
  161. {
  162. myException = e;
  163. broadcast.setState(BroadcastState.ABORTED, e.errorCodeText, e.errorText);
  164. CommonLogger.alarm.error("Broadcast aborted: " + e.getMessage());
  165. myLogger.error("Broadcast aborted", e);
  166. return;
  167. }
  168. catch (Throwable t)
  169. {
  170. // Caught stray unexpected runtime problem
  171. CommonLogger.alarm.error("Broadcast aborted: " + t);
  172. myLogger.error("Broadcast aborted", t);
  173. myException = new BroadcastException(BroadcastError.PLATFORM_ERROR, t.getMessage());
  174. broadcast.setState(BroadcastState.ABORTED, myException.errorCodeText, myException.errorText);
  175. }
  176. finally
  177. {
  178. // Put broadcast in broadcasts map.
  179. String broadcastId = broadcast.getBroadcastId();
  180. if (broadcastId.length() != 0)
  181. {
  182. broadcasts.put(broadcastId, broadcast);
  183. }
  184. else
  185. {
  186. String makeUpId = "Unknown" + unknownBroadcastIdNdx++;
  187. broadcasts.put(makeUpId, broadcast);
  188. }
  189. // Return regular or error response
  190. String responseXML = broadcast.getResponseXML(myException);
  191. PrintWriter writer = response.getWriter();
  192. writer.write(responseXML);
  193. writer.close();
  194. }
  195. try
  196. {
  197. broadcast.initAsync();
  198. broadcast.startProcessing();
  199. }
  200. catch (BroadcastException e)
  201. {
  202. broadcast.setState(BroadcastState.ABORTED, e.errorCodeText, e.errorText);
  203. CommonLogger.alarm.error("Broadcast aborted: " + e.getMessage());
  204. myLogger.error("Broadcast aborted", e);
  205. }
  206. }
  207. catch (Throwable t)
  208. {
  209. // Caught stray unexpected runtime problem
  210. CommonLogger.alarm.error("Broadcast aborted: " + t.getMessage());
  211. myLogger.error("Broadcast aborted", t);
  212. }
  213. }
  214. /**
  215. * Functions covered are
  216. * get=status
  217. * get=cancel_broadcast&broadcast_id=XXX
  218. */
  219. @Override
  220. protected void doGet(HttpServletRequest request, HttpServletResponse response)
  221. {
  222. PrintWriter out;
  223. try
  224. {
  225. out = response.getWriter();
  226. }
  227. catch (IOException e)
  228. {
  229. CommonLogger.alarm.error("Cannot write a reply: " + e);
  230. return;
  231. }
  232. String get = (String)request.getParameter("get");
  233. if (get == null)
  234. {
  235. // Return http status BAD REQUEST
  236. int httpStatus = HttpServletResponse.SC_BAD_REQUEST;
  237. try
  238. {
  239. response.sendError(httpStatus);
  240. }
  241. catch (IOException e)
  242. {
  243. myLogger.warn("Unnable to return HTTP error code " + httpStatus);
  244. }
  245. return;
  246. }
  247. if (get.equalsIgnoreCase("status"))
  248. {
  249. getStatus(request, out);
  250. }
  251. else if (get.equalsIgnoreCase("cancel_broadcast"))
  252. {
  253. cancelBroadcast(request, out);
  254. }
  255. else if (get.equalsIgnoreCase("pause_broadcast"))
  256. {
  257. pauseBroadcast(request, out);
  258. }
  259. else if (get.equalsIgnoreCase("resume_broadcast"))
  260. {
  261. resumeBroadcast(request, out);
  262. }
  263. else
  264. {
  265. out.write(get + " not supported");
  266. }
  267. out.close();
  268. }
  269. private void cancelBroadcast(HttpServletRequest request, PrintWriter out)
  270. {
  271. // Get broadcastId from request
  272. String broadcastId = getBroadcastId(request);
  273. Broadcast broadcast = broadcasts.get(broadcastId);
  274. if (broadcast == null)
  275. {
  276. out.format("Broadcast %s does not exist", broadcastId);
  277. return;
  278. }
  279. broadcast.cancel();
  280. }
  281. protected void pauseBroadcast(HttpServletRequest request, PrintWriter out)
  282. {
  283. // Get broadcastId from request
  284. String broadcastId = getBroadcastId(request);
  285. Broadcast broadcast = broadcasts.get(broadcastId);
  286. if (broadcast == null)
  287. {
  288. out.format("Broadcast %s does not exist", broadcastId);
  289. return;
  290. }
  291. broadcast.pause();
  292. }
  293. protected void resumeBroadcast(HttpServletRequest request, PrintWriter out)
  294. {
  295. // Get broadcastId from request
  296. String broadcastId = getBroadcastId(request);
  297. Broadcast broadcast = broadcasts.get(broadcastId);
  298. if (broadcast == null)
  299. {
  300. out.format("Broadcast %s does not exist", broadcastId);
  301. return;
  302. }
  303. broadcast.resume();
  304. }
  305. /**
  306. * <CallEngine_status>
  307. * status of each broadcast
  308. * <calls><total>ttt</total><connected>nnn</connected>
  309. * </CallEngine_status>
  310. */
  311. private void getStatus(HttpServletRequest request, PrintWriter out)
  312. {
  313. String broadcastId = request.getParameter("broadcast_id");
  314. if (broadcastId != null)
  315. {
  316. broadcastId = broadcastId.trim();
  317. if (broadcastId.length() == 0)
  318. {
  319. out.write("broadcast_id request parameter cannot be empty");
  320. return;
  321. }
  322. Broadcast broadcast = broadcasts.get(broadcastId);
  323. if (broadcast == null)
  324. {
  325. out.write("<error>No such broadcast</error>");
  326. }
  327. else
  328. {
  329. out.write(broadcast.mkStatusReport());
  330. }
  331. return;
  332. }
  333. else
  334. {
  335. String tag = engineName + "_status";
  336. out.write("<" + tag + ">\r\n");
  337. out.write("<startup_time>" + startupTimestamp
  338. + "</startup_time>\r\n");
  339. // First get a copy of broadcasts, to avoid mutex deadlock.
  340. Vector<Broadcast> broadcastList = new Vector<Broadcast>();
  341. synchronized(broadcasts)
  342. {
  343. for (String key : broadcasts.keySet())
  344. {
  345. broadcastList.add(broadcasts.get(key));
  346. }
  347. }
  348. // We have released the lock.
  349. // Then append status of each broadcast to outBuf.
  350. for (Broadcast broadcast : broadcastList)
  351. {
  352. out.write(broadcast.mkStatusReport());
  353. }
  354. out.write("<job_summary completed='" + getCompletedJobCount() + "' active='" + getActiveJobCount() + "' ready='" + getReadyJobCount() + "'/>");
  355. out.write("</" + tag + ">");
  356. }
  357. }
  358. public int getReadyJobCount()
  359. {
  360. int readyCount = 0;
  361. synchronized(broadcasts)
  362. {
  363. for (Broadcast broadcast : broadcasts.values())
  364. {
  365. readyCount += broadcast.getReadyJobCount();
  366. }
  367. }
  368. return readyCount;
  369. }
  370. public int getActiveJobCount()
  371. {
  372. int activeCount = 0;
  373. synchronized(broadcasts)
  374. {
  375. for (Broadcast broadcast : broadcasts.values())
  376. {
  377. activeCount += broadcast.getActiveJobCount();
  378. }
  379. }
  380. return activeCount;
  381. }
  382. public int getCompletedJobCount()
  383. {
  384. int additionalCompletedJobCount = 0;
  385. synchronized(broadcasts)
  386. {
  387. for (Broadcast broadcast : broadcasts.values())
  388. {
  389. additionalCompletedJobCount += broadcast.getCompletedJobCount();
  390. }
  391. }
  392. return completedJobCount + additionalCompletedJobCount;
  393. }
  394. public void removeBroadcast(String broadcastId)
  395. {
  396. CommonLogger.activity.info("Removing broadcast " + broadcastId);
  397. synchronized(broadcasts)
  398. {
  399. broadcasts.remove(broadcastId);
  400. }
  401. }
  402. public boolean notInService()
  403. {
  404. return notInService;
  405. }
  406. /**
  407. * Decode http GET request for broadcast_id value
  408. * @param request
  409. * @return broadcast_id
  410. */
  411. private String getBroadcastId(HttpServletRequest request)
  412. {
  413. return request.getParameter("broadcast_id");
  414. }
  415. /**
  416. * Invoked by servlet container when servlet is destroyed.
  417. */
  418. public final void destroy()
  419. {
  420. System.out.println(engineName + " destroyed");
  421. // Kill threads in each PostBack, which is remembered in postBackMap.
  422. for (PostBack postback : postBackMap.values())
  423. {
  424. postback.terminate();
  425. }
  426. for (Broadcast broadcast : broadcasts.values())
  427. {
  428. broadcast.terminate(BroadcastState.ABORTED, "Platform termination");
  429. }
  430. destroyChild();
  431. }
  432. /**
  433. * Indirectly invoked by servlet container during servlet initialization.
  434. */
  435. abstract protected void initChild();
  436. /**
  437. * Indirectly invoked by serlet container during destruction of servlet.
  438. */
  439. abstract protected void destroyChild();
  440. }