|
- package altk.comm.engine;
-
- import java.io.ByteArrayInputStream;
- import java.io.IOException;
- import java.io.InputStream;
- import java.util.HashMap;
- import java.util.Map;
- import java.util.Vector;
- import java.util.zip.ZipInputStream;
-
- import javax.servlet.http.HttpServletRequest;
- import javax.xml.parsers.DocumentBuilder;
- import javax.xml.parsers.DocumentBuilderFactory;
- import javax.xml.xpath.XPath;
- import javax.xml.xpath.XPathConstants;
- import javax.xml.xpath.XPathExpressionException;
- import javax.xml.xpath.XPathFactory;
-
- import org.w3c.dom.Document;
- import org.w3c.dom.Element;
- import org.w3c.dom.NamedNodeMap;
- import org.w3c.dom.Node;
- import org.w3c.dom.NodeList;
- import org.xml.sax.SAXException;
-
- import altk.comm.engine.exception.BroadcastError;
- import altk.comm.engine.exception.BroadcastException;
- import altk.comm.engine.exception.BroadcastMsgException;
- import altk.comm.engine.exception.EngineException;
- import altk.comm.engine.exception.InternalErrorException;
- import altk.comm.engine.exception.NonExistentException;
- import altk.comm.engine.exception.PlatformError;
- import altk.comm.engine.exception.PlatformException;
-
- /**
- * Abstract class extending Broadcast by providing an implementation of the decode
- * method to parse HTTP POST input into XML DOM, and extracting the required class
- * attributes broadcast_id, launch_record_id, expire_time, postback and recipientList.
- * @author Yuk-Ming
- *
- */
- public abstract class XMLDOMBroadcast extends Broadcast
- {
- protected XPath xpathEngine;
- protected DocumentBuilder builder;
- protected Element broadcastNode;
-
- /**
- *
- * @param broadcastType
- * @param activityRecordIdParamName - if null, default is used
- * @param jobReportRootNodeName - job report root node name
- */
- protected XMLDOMBroadcast(String broadcastType, String activityRecordIdParamName,
- String jobReportRootNodeName)
- {
- super(broadcastType, activityRecordIdParamName, jobReportRootNodeName);
- }
-
- /**
- * Sets up XMLDoc and parses broadcast_id, expire_time, postBackUrl and recipientList.
- * Derived class cannot override this method, which is final.
- * @throws EngineException
- */
- @Override
- protected final void decode(HttpServletRequest request, boolean notInService) throws EngineException
- {
- try
- {
- DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
- builder = factory.newDocumentBuilder();
- xpathEngine = XPathFactory.newInstance().newXPath();
- }
- catch (Exception e)
- {
- CommonLogger.alarm.error(e.getMessage());
- throw new PlatformException(PlatformError.RUNTIME_ERROR, e.getMessage());
- }
-
-
- // parse request into multiple BroadcastSMS pass then to Dispatcher
- Document xmlDoc;
- try
- {
- InputStream inb;
- String contentType = request.getContentType();
- if (contentType != null && contentType.split("/")[1].equalsIgnoreCase("zip"))
- {
- ZipInputStream zip = new ZipInputStream(request.getInputStream());
- zip.getNextEntry();
- inb = zip;
- }
- else
- {
- inb = request.getInputStream();
- }
- int contentLength = request.getContentLength();
- CommonLogger.activity.info("Receiving " + contentLength + " bytes of data");
- byte[] content = new byte[contentLength];
- int offset = 0;
- int length = contentLength;
- int read;
- while (length > 0)
- {
- read = inb.read(content, offset, length);
- if (read < 0) break;
- offset += read;
- length -= read;
- }
- CommonLogger.activity.info("Received: " + new String(content, "UTF-8"));
- xmlDoc = builder.parse(new ByteArrayInputStream(content), "UTF-8");
- }
- catch (SAXException e)
- {
- throw new BroadcastException(BroadcastError.BAD_REQUEST_DATA, e.getMessage());
- }
- catch (IOException e)
- {
- throw new BroadcastException(BroadcastError.READ_REQUEST_ERROR, "Problem in reading request");
- }
-
- //String xpath = "//" + broadcastName; // get all first level elements
- NodeList broadcastNodes = null;
- broadcastNodes = xmlDoc.getElementsByTagName(broadcastType);
- if (broadcastNodes.getLength() == 0)
- {
- throw new BroadcastException(BroadcastError.BAD_REQUEST_DATA, "No <" + broadcastType + "> tag");
- }
-
- if (notInService) return;
-
- // Only one broadcast node is processed. The rest are silently ignored.
- broadcastNode = (Element)broadcastNodes.item(0);
- if (!broadcastType.equals(broadcastNode.getNodeName()))
- {
- throw new BroadcastMsgException("Broadcast node name does not match " + broadcastType);
- }
-
- // broadcast_id
- String broadcastId = broadcastNode.getAttribute("broadcast_id");
- if (broadcastId.length() == 0)
- {
- throw new BroadcastMsgException("Missing broadcast_id");
- }
- setBroadcastId(broadcastId);
-
- // expireTime
- long now = System.currentTimeMillis();
- try
- {
- long expireTime = getLongValue("expire_time", broadcastNode);
- // defaults to 20 minutes
- myLogger.debug("expire_time decoded to be " + expireTime);
- if (expireTime == 0) expireTime = now + 20 * 60 * 1000;
- myLogger.debug("expire time adjusted to be " + expireTime);
- setExpireTime(expireTime);
-
- setLaunchRecordId(broadcastNode.getAttribute("launch_record_id"));
- }
- catch (Exception e)
- {
- throw new BroadcastMsgException("Missing or bad expire_time");
- }
-
- // Postback
- postBackURL = getStringValue("async_status_post_back", broadcastNode);
- if (postBackURL != null && (postBackURL=postBackURL.trim()).length() == 0) postBackURL = null;
- if (postBackURL == null)
- {
- CommonLogger.alarm.warn("Missing asyn_status_post_back in POST data");
- }
-
- NodeList recipientNodes = null;
- String xpath = "recipient_list/recipient";
- try
- {
- recipientNodes = (NodeList) xpathEngine.evaluate(xpath,
- broadcastNode, XPathConstants.NODESET);
- }
- catch (XPathExpressionException e)
- {
- throw new InternalErrorException("Bad xpath 'recipient_list/recipient", e);
- }
- Element recipientNode;
- String contact_id;
- String activity_record_id;
- xpath = "email";
- for (int i = 0; i < recipientNodes.getLength(); i++)
- {
- recipientNode = (Element)recipientNodes.item(i);
- contact_id = recipientNode.getAttribute("contact_id");
- if (contact_id == null || (contact_id=contact_id.trim()).length() == 0)
- {
- myLogger.warn("Missing or empty contact_id for a recipient in broadcast " + getBroadcastId());
- continue;
- }
- activity_record_id = recipientNode.getAttribute(activityRecordIdParamName);
- if (activity_record_id == null || (activity_record_id = activity_record_id.trim()).length() == 0)
- {
- throw new BroadcastMsgException("Missing or empty " + getActivityRecordIdName() + " attribute for a recipient in broadcast " + getBroadcastId());
- }
-
- Map<String, String> attributes = new HashMap<String, String>();
- NodeList children = recipientNode.getChildNodes();
- for (int j = 0; j < children.getLength(); j++)
- {
- Node child = children.item(j);
- if (child.getNodeType() != Node.ELEMENT_NODE) continue;
- attributes.put(child.getNodeName(), child.getTextContent());
- }
- recipientList.add(new Recipient(contact_id, activity_record_id, attributes));
- }
- }
-
- /**
- * Gets String value of child with given name.
- * @param childNodeName
- * @param element
- * @return null if no such child exists.
- * @throws IllegalArgumentException when element is null
- */
- protected String getStringValue(String childNodeName, Element element)
- throws IllegalArgumentException
- {
- if (element == null)
- {
- throw new IllegalArgumentException("Element cannot be null");
- }
- NodeList children = element.getChildNodes();
- for (int i = 0; i < children.getLength(); i++)
- {
- Node child = children.item(i);
- if (child.getNodeType() == Node.ELEMENT_NODE &&
- child.getNodeName().equals(childNodeName))
- {
- return child.getTextContent();
- }
- }
- return null;
- }
-
- /**
- *
- * @param childNodeName
- * @param element
- * @return
- * @throws IllegalArgumentException when element is null
- */
- protected String getNonNullStringValue(String childNodeName, Element element)
- throws IllegalArgumentException
- {
- String value = getStringValue(childNodeName, element);
- return value==null?"":value;
- }
-
- /**
- *
- * @param childNodeName
- * @param element
- * @return
- * @throws IllegalArgumentException when element is null
- */
- public boolean getBooleanValue(String childNodeName, Element element)
- throws IllegalArgumentException
- {
- String str = getStringValue(childNodeName, element);
- if (str == null) return false;
- return Boolean.parseBoolean(str.trim());
- }
-
- /**
- *
- * @param childNodeName
- * @param element
- * @return
- * @throws NonExistentException
- * @throws NumberFormatException
- * @throws IllegalArgumentException when element is null
- */
- protected int getIntValue(String childNodeName, Element element)
- throws NonExistentException, NumberFormatException, IllegalArgumentException
- {
- if (element == null)
- {
- throw new IllegalArgumentException("Element cannot be null, when invoking getIntValue method");
- }
- try
- {
- String str = getStringValue(childNodeName, element);
- if (str == null)
- {
- throw new NonExistentException("No child \"" + childNodeName + "\" exists for element \"" + xml2Str(element) + "\"");
- }
- return Integer.parseInt(str);
- }
- catch (NumberFormatException e)
- {
- throw new NumberFormatException("Value of child \"" + childNodeName + "\" not integer for element \"" + xml2Str(element) + "\"");
- }
- }
-
- /**
- *
- * @param childNodeName
- * @param element
- * @return
- * @throws NonExistentException
- * @throws NumberFormatException
- * @throws IllegalArgumentException when element is null
- */
- protected Long getLongValue(String childNodeName, Element element)
- throws NonExistentException, NumberFormatException, IllegalArgumentException
- {
- if (element == null)
- {
- throw new IllegalArgumentException("Element cannot be null, when invoking getIntValue method");
- }
- try
- {
- String str = getStringValue(childNodeName, element);
- if (str == null)
- {
- throw new NonExistentException("No child \"" + childNodeName + "\" exists for element \"" + xml2Str(element) + "\"");
- }
- return Long.parseLong(str);
- }
- catch (NumberFormatException e)
- {
- throw new NumberFormatException("Value of child \"" + childNodeName + "\" not integer for element \"" + xml2Str(element) + "\"");
- }
- }
-
- protected NodeList getNodeList(String xpath, Element element)
- {
- try
- {
- NodeList nodes = (NodeList) xpathEngine.evaluate(xpath, element, XPathConstants.NODESET);
- return nodes;
- }
- catch (XPathExpressionException e)
- {
- throw new InternalErrorException("Improper xpath \"" + xpath + "\"", e);
- }
- }
-
- protected Node getNode(String xpath, Element element)
- {
- try
- {
- Node node = (Node) xpathEngine.evaluate(xpath, element, XPathConstants.NODE);
- return node;
- }
- catch (XPathExpressionException e)
- {
- throw new InternalErrorException("Improper xpath \"" + xpath + "\"", e);
- }
- }
-
- static public void appendXML(Element topElement, StringBuffer buf)
- {
- // starting tag
- buf.append('<');
- String elementName = topElement.getNodeName();
- buf.append(elementName);
-
- // handle attributes first
- NamedNodeMap attributes = topElement.getAttributes();
- for (int i = 0; i < attributes.getLength(); i++)
- {
- Node attr = attributes.item(i);
- buf.append(' ');
- buf.append(attr.getNodeName());
- buf.append("=\"");
- buf.append(attr.getNodeValue());
- buf.append("\"");
- }
- buf.append('>');
-
- // handling text
- Vector<Node> childElements = new Vector<Node>();
- NodeList children = topElement.getChildNodes();
- for (int i = 0; i < children.getLength(); i++)
- {
- Node node = children.item(i);
- switch (node.getNodeType())
- {
- case Node.ELEMENT_NODE:
- // defer handling
- childElements.add(node);
- break;
- case Node.TEXT_NODE:
- buf.append(node.getNodeValue());
- break;
- default:
- }
- }
- // handling children elements
- for (Node child : childElements)
- {
- appendXML((Element)child, buf);
- }
- // ending tag
- buf.append("</"); buf.append(elementName); buf.append('>');
- }
-
- public String xml2Str(Element element)
- {
- StringBuffer buf = new StringBuffer();
- appendXML(element, buf);
- return buf.toString();
- }
-
- /**
- * Derived class may override this method to redefine the name of activity_record_id,
- * e.g. email_record_id, sms_record_id, etc.
- * @return name of activity_record_9d.
- */
- protected String getActivityRecordIdName()
- {
- return "activity_record_id";
- }
-
- }
|