Imported from dev1.link2tek.net CommEngine.git
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

424 行
14 KiB

  1. package altk.comm.engine;
  2. import java.io.ByteArrayInputStream;
  3. import java.io.IOException;
  4. import java.io.InputStream;
  5. import java.util.HashMap;
  6. import java.util.Map;
  7. import java.util.Vector;
  8. import java.util.zip.ZipInputStream;
  9. import javax.servlet.http.HttpServletRequest;
  10. import javax.xml.parsers.DocumentBuilder;
  11. import javax.xml.parsers.DocumentBuilderFactory;
  12. import javax.xml.xpath.XPath;
  13. import javax.xml.xpath.XPathConstants;
  14. import javax.xml.xpath.XPathExpressionException;
  15. import javax.xml.xpath.XPathFactory;
  16. import org.w3c.dom.Document;
  17. import org.w3c.dom.Element;
  18. import org.w3c.dom.NamedNodeMap;
  19. import org.w3c.dom.Node;
  20. import org.w3c.dom.NodeList;
  21. import org.xml.sax.SAXException;
  22. import altk.comm.engine.exception.BroadcastError;
  23. import altk.comm.engine.exception.BroadcastException;
  24. import altk.comm.engine.exception.BroadcastMsgException;
  25. import altk.comm.engine.exception.EngineException;
  26. import altk.comm.engine.exception.InternalErrorException;
  27. import altk.comm.engine.exception.NonExistentException;
  28. import altk.comm.engine.exception.PlatformError;
  29. import altk.comm.engine.exception.PlatformException;
  30. /**
  31. * Abstract class extending Broadcast by providing an implementation of the decode
  32. * method to parse HTTP POST input into XML DOM, and extracting the required class
  33. * attributes broadcast_id, launch_record_id, expire_time, postback and recipientList.
  34. * @author Yuk-Ming
  35. *
  36. */
  37. public abstract class XMLDOMBroadcast extends Broadcast
  38. {
  39. protected XPath xpathEngine;
  40. protected DocumentBuilder builder;
  41. protected Element broadcastNode;
  42. /**
  43. *
  44. * @param broadcastType
  45. * @param activityRecordIdParamName - if null, default is used
  46. * @param jobReportRootNodeName - job report root node name
  47. */
  48. protected XMLDOMBroadcast(String broadcastType, String activityRecordIdParamName,
  49. String jobReportRootNodeName)
  50. {
  51. super(broadcastType, activityRecordIdParamName, jobReportRootNodeName);
  52. }
  53. /**
  54. * Sets up XMLDoc and parses broadcast_id, expire_time, postBackUrl and recipientList.
  55. * Derived class cannot override this method, which is final.
  56. * @throws EngineException
  57. */
  58. @Override
  59. protected final void decode(HttpServletRequest request, boolean notInService) throws EngineException
  60. {
  61. try
  62. {
  63. DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
  64. builder = factory.newDocumentBuilder();
  65. xpathEngine = XPathFactory.newInstance().newXPath();
  66. }
  67. catch (Exception e)
  68. {
  69. CommonLogger.alarm.error(e.getMessage());
  70. throw new PlatformException(PlatformError.RUNTIME_ERROR, e.getMessage());
  71. }
  72. // parse request into multiple BroadcastSMS pass then to Dispatcher
  73. Document xmlDoc;
  74. try
  75. {
  76. InputStream inb;
  77. String contentType = request.getContentType();
  78. if (contentType != null && contentType.split("/")[1].equalsIgnoreCase("zip"))
  79. {
  80. ZipInputStream zip = new ZipInputStream(request.getInputStream());
  81. zip.getNextEntry();
  82. inb = zip;
  83. }
  84. else
  85. {
  86. inb = request.getInputStream();
  87. }
  88. int contentLength = request.getContentLength();
  89. CommonLogger.activity.info("Receiving " + contentLength + " bytes of data");
  90. byte[] content = new byte[contentLength];
  91. int offset = 0;
  92. int length = contentLength;
  93. int read;
  94. while (length > 0)
  95. {
  96. read = inb.read(content, offset, length);
  97. if (read < 0) break;
  98. offset += read;
  99. length -= read;
  100. }
  101. CommonLogger.activity.info("Received: " + new String(content, "UTF-8"));
  102. xmlDoc = builder.parse(new ByteArrayInputStream(content), "UTF-8");
  103. }
  104. catch (SAXException e)
  105. {
  106. throw new BroadcastException(BroadcastError.BAD_REQUEST_DATA, e.getMessage());
  107. }
  108. catch (IOException e)
  109. {
  110. throw new BroadcastException(BroadcastError.READ_REQUEST_ERROR, "Problem in reading request");
  111. }
  112. //String xpath = "//" + broadcastName; // get all first level elements
  113. NodeList broadcastNodes = null;
  114. broadcastNodes = xmlDoc.getElementsByTagName(broadcastType);
  115. if (broadcastNodes.getLength() == 0)
  116. {
  117. throw new BroadcastException(BroadcastError.BAD_REQUEST_DATA, "No <" + broadcastType + "> tag");
  118. }
  119. if (notInService) return;
  120. // Only one broadcast node is processed. The rest are silently ignored.
  121. broadcastNode = (Element)broadcastNodes.item(0);
  122. if (!broadcastType.equals(broadcastNode.getNodeName()))
  123. {
  124. throw new BroadcastMsgException("Broadcast node name does not match " + broadcastType);
  125. }
  126. // broadcast_id
  127. String broadcastId = broadcastNode.getAttribute("broadcast_id");
  128. if (broadcastId.length() == 0)
  129. {
  130. throw new BroadcastMsgException("Missing broadcast_id");
  131. }
  132. setBroadcastId(broadcastId);
  133. // expireTime
  134. long now = System.currentTimeMillis();
  135. try
  136. {
  137. long expireTime = getLongValue("expire_time", broadcastNode);
  138. // defaults to 20 minutes
  139. myLogger.debug("expire_time decoded to be " + expireTime);
  140. if (expireTime == 0) expireTime = now + 20 * 60 * 1000;
  141. myLogger.debug("expire time adjusted to be " + expireTime);
  142. setExpireTime(expireTime);
  143. setLaunchRecordId(broadcastNode.getAttribute("launch_record_id"));
  144. }
  145. catch (Exception e)
  146. {
  147. throw new BroadcastMsgException("Missing or bad expire_time");
  148. }
  149. // Postback
  150. postBackURL = getStringValue("async_status_post_back", broadcastNode);
  151. if (postBackURL != null && (postBackURL=postBackURL.trim()).length() == 0) postBackURL = null;
  152. if (postBackURL == null)
  153. {
  154. CommonLogger.alarm.warn("Missing asyn_status_post_back in POST data");
  155. }
  156. NodeList recipientNodes = null;
  157. String xpath = "recipient_list/recipient";
  158. try
  159. {
  160. recipientNodes = (NodeList) xpathEngine.evaluate(xpath,
  161. broadcastNode, XPathConstants.NODESET);
  162. }
  163. catch (XPathExpressionException e)
  164. {
  165. throw new InternalErrorException("Bad xpath 'recipient_list/recipient", e);
  166. }
  167. Element recipientNode;
  168. String contact_id;
  169. String activity_record_id;
  170. xpath = "email";
  171. for (int i = 0; i < recipientNodes.getLength(); i++)
  172. {
  173. recipientNode = (Element)recipientNodes.item(i);
  174. contact_id = recipientNode.getAttribute("contact_id");
  175. if (contact_id == null || (contact_id=contact_id.trim()).length() == 0)
  176. {
  177. myLogger.warn("Missing or empty contact_id for a recipient in broadcast " + getBroadcastId());
  178. continue;
  179. }
  180. activity_record_id = recipientNode.getAttribute(activityRecordIdParamName);
  181. if (activity_record_id == null || (activity_record_id = activity_record_id.trim()).length() == 0)
  182. {
  183. throw new BroadcastMsgException("Missing or empty " + getActivityRecordIdName() + " attribute for a recipient in broadcast " + getBroadcastId());
  184. }
  185. Map<String, String> attributes = new HashMap<String, String>();
  186. NodeList children = recipientNode.getChildNodes();
  187. for (int j = 0; j < children.getLength(); j++)
  188. {
  189. Node child = children.item(j);
  190. if (child.getNodeType() != Node.ELEMENT_NODE) continue;
  191. attributes.put(child.getNodeName(), child.getTextContent());
  192. }
  193. recipientList.add(new Recipient(contact_id, activity_record_id, attributes));
  194. }
  195. }
  196. /**
  197. * Gets String value of child with given name.
  198. * @param childNodeName
  199. * @param element
  200. * @return null if no such child exists.
  201. * @throws IllegalArgumentException when element is null
  202. */
  203. protected String getStringValue(String childNodeName, Element element)
  204. throws IllegalArgumentException
  205. {
  206. if (element == null)
  207. {
  208. throw new IllegalArgumentException("Element cannot be null");
  209. }
  210. NodeList children = element.getChildNodes();
  211. for (int i = 0; i < children.getLength(); i++)
  212. {
  213. Node child = children.item(i);
  214. if (child.getNodeType() == Node.ELEMENT_NODE &&
  215. child.getNodeName().equals(childNodeName))
  216. {
  217. return child.getTextContent();
  218. }
  219. }
  220. return null;
  221. }
  222. /**
  223. *
  224. * @param childNodeName
  225. * @param element
  226. * @return
  227. * @throws IllegalArgumentException when element is null
  228. */
  229. protected String getNonNullStringValue(String childNodeName, Element element)
  230. throws IllegalArgumentException
  231. {
  232. String value = getStringValue(childNodeName, element);
  233. return value==null?"":value;
  234. }
  235. /**
  236. *
  237. * @param childNodeName
  238. * @param element
  239. * @return
  240. * @throws IllegalArgumentException when element is null
  241. */
  242. public boolean getBooleanValue(String childNodeName, Element element)
  243. throws IllegalArgumentException
  244. {
  245. String str = getStringValue(childNodeName, element);
  246. if (str == null) return false;
  247. return Boolean.parseBoolean(str.trim());
  248. }
  249. /**
  250. *
  251. * @param childNodeName
  252. * @param element
  253. * @return
  254. * @throws NonExistentException
  255. * @throws NumberFormatException
  256. * @throws IllegalArgumentException when element is null
  257. */
  258. protected int getIntValue(String childNodeName, Element element)
  259. throws NonExistentException, NumberFormatException, IllegalArgumentException
  260. {
  261. if (element == null)
  262. {
  263. throw new IllegalArgumentException("Element cannot be null, when invoking getIntValue method");
  264. }
  265. try
  266. {
  267. String str = getStringValue(childNodeName, element);
  268. if (str == null)
  269. {
  270. throw new NonExistentException("No child \"" + childNodeName + "\" exists for element \"" + xml2Str(element) + "\"");
  271. }
  272. return Integer.parseInt(str);
  273. }
  274. catch (NumberFormatException e)
  275. {
  276. throw new NumberFormatException("Value of child \"" + childNodeName + "\" not integer for element \"" + xml2Str(element) + "\"");
  277. }
  278. }
  279. /**
  280. *
  281. * @param childNodeName
  282. * @param element
  283. * @return
  284. * @throws NonExistentException
  285. * @throws NumberFormatException
  286. * @throws IllegalArgumentException when element is null
  287. */
  288. protected Long getLongValue(String childNodeName, Element element)
  289. throws NonExistentException, NumberFormatException, IllegalArgumentException
  290. {
  291. if (element == null)
  292. {
  293. throw new IllegalArgumentException("Element cannot be null, when invoking getIntValue method");
  294. }
  295. try
  296. {
  297. String str = getStringValue(childNodeName, element);
  298. if (str == null)
  299. {
  300. throw new NonExistentException("No child \"" + childNodeName + "\" exists for element \"" + xml2Str(element) + "\"");
  301. }
  302. return Long.parseLong(str);
  303. }
  304. catch (NumberFormatException e)
  305. {
  306. throw new NumberFormatException("Value of child \"" + childNodeName + "\" not integer for element \"" + xml2Str(element) + "\"");
  307. }
  308. }
  309. protected NodeList getNodeList(String xpath, Element element)
  310. {
  311. try
  312. {
  313. NodeList nodes = (NodeList) xpathEngine.evaluate(xpath, element, XPathConstants.NODESET);
  314. return nodes;
  315. }
  316. catch (XPathExpressionException e)
  317. {
  318. throw new InternalErrorException("Improper xpath \"" + xpath + "\"", e);
  319. }
  320. }
  321. protected Node getNode(String xpath, Element element)
  322. {
  323. try
  324. {
  325. Node node = (Node) xpathEngine.evaluate(xpath, element, XPathConstants.NODE);
  326. return node;
  327. }
  328. catch (XPathExpressionException e)
  329. {
  330. throw new InternalErrorException("Improper xpath \"" + xpath + "\"", e);
  331. }
  332. }
  333. static public void appendXML(Element topElement, StringBuffer buf)
  334. {
  335. // starting tag
  336. buf.append('<');
  337. String elementName = topElement.getNodeName();
  338. buf.append(elementName);
  339. // handle attributes first
  340. NamedNodeMap attributes = topElement.getAttributes();
  341. for (int i = 0; i < attributes.getLength(); i++)
  342. {
  343. Node attr = attributes.item(i);
  344. buf.append(' ');
  345. buf.append(attr.getNodeName());
  346. buf.append("=\"");
  347. buf.append(attr.getNodeValue());
  348. buf.append("\"");
  349. }
  350. buf.append('>');
  351. // handling text
  352. Vector<Node> childElements = new Vector<Node>();
  353. NodeList children = topElement.getChildNodes();
  354. for (int i = 0; i < children.getLength(); i++)
  355. {
  356. Node node = children.item(i);
  357. switch (node.getNodeType())
  358. {
  359. case Node.ELEMENT_NODE:
  360. // defer handling
  361. childElements.add(node);
  362. break;
  363. case Node.TEXT_NODE:
  364. buf.append(node.getNodeValue());
  365. break;
  366. default:
  367. }
  368. }
  369. // handling children elements
  370. for (Node child : childElements)
  371. {
  372. appendXML((Element)child, buf);
  373. }
  374. // ending tag
  375. buf.append("</"); buf.append(elementName); buf.append('>');
  376. }
  377. public String xml2Str(Element element)
  378. {
  379. StringBuffer buf = new StringBuffer();
  380. appendXML(element, buf);
  381. return buf.toString();
  382. }
  383. /**
  384. * Derived class may override this method to redefine the name of activity_record_id,
  385. * e.g. email_record_id, sms_record_id, etc.
  386. * @return name of activity_record_9d.
  387. */
  388. protected String getActivityRecordIdName()
  389. {
  390. return "activity_record_id";
  391. }
  392. }