[DMAAP-48] Initial code import
[dmaap/datarouter.git] / datarouter-prov / src / main / java / com / att / research / datarouter / provisioning / PublishServlet.java
diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/PublishServlet.java b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/PublishServlet.java
new file mode 100644 (file)
index 0000000..2a8e2e3
--- /dev/null
@@ -0,0 +1,192 @@
+/*******************************************************************************\r
+ * ============LICENSE_START==================================================\r
+ * * org.onap.dmaap\r
+ * * ===========================================================================\r
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.\r
+ * * ===========================================================================\r
+ * * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * * you may not use this file except in compliance with the License.\r
+ * * You may obtain a copy of the License at\r
+ * * \r
+ *  *      http://www.apache.org/licenses/LICENSE-2.0\r
+ * * \r
+ *  * Unless required by applicable law or agreed to in writing, software\r
+ * * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * * See the License for the specific language governing permissions and\r
+ * * limitations under the License.\r
+ * * ============LICENSE_END====================================================\r
+ * *\r
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.\r
+ * *\r
+ ******************************************************************************/\r
+\r
+\r
+package com.att.research.datarouter.provisioning;\r
+\r
+import java.io.IOException;\r
+import java.io.InputStream;\r
+import java.util.ArrayList;\r
+import java.util.Collection;\r
+import java.util.List;\r
+import java.util.Properties;\r
+\r
+import javax.servlet.ServletConfig;\r
+import javax.servlet.ServletException;\r
+import javax.servlet.http.HttpServletRequest;\r
+import javax.servlet.http.HttpServletResponse;\r
+\r
+import org.json.JSONArray;\r
+import org.json.JSONObject;\r
+import org.json.JSONTokener;\r
+\r
+import com.att.eelf.configuration.EELFLogger;\r
+import com.att.eelf.configuration.EELFManager;\r
+import com.att.research.datarouter.provisioning.beans.EventLogRecord;\r
+import com.att.research.datarouter.provisioning.beans.Feed;\r
+import com.att.research.datarouter.provisioning.beans.IngressRoute;\r
+import com.att.research.datarouter.provisioning.eelf.EelfMsgs;\r
+import com.att.research.datarouter.provisioning.utils.*;\r
+\r
+/**\r
+ * This servlet handles redirects for the <publishURL> on the provisioning server,\r
+ * which is generated by the provisioning server to handle a particular subscriptions to a feed.\r
+ * See the <b>File Publishing and Delivery API</b> document for details on how these methods\r
+ * should be invoked.\r
+ *\r
+ * @author Robert Eby\r
+ * @version $Id: PublishServlet.java,v 1.8 2014/03/12 19:45:41 eby Exp $\r
+ */\r
+@SuppressWarnings("serial")\r
+public class PublishServlet extends BaseServlet {\r
+       private int next_node;\r
+       private String provstring;\r
+       private List<IngressRoute> irt;\r
+       //Adding EELF Logger Rally:US664892  \r
+    private static EELFLogger eelflogger = EELFManager.getInstance().getLogger("com.att.research.datarouter.provisioning.PublishServlet");\r
+    \r
+\r
+       @Override\r
+       public void init(ServletConfig config) throws ServletException {\r
+               super.init(config);\r
+               next_node = 0;\r
+               provstring = "";\r
+               irt = new ArrayList<IngressRoute>();\r
+       \r
+       }\r
+       @Override\r
+       public void doDelete(HttpServletRequest req, HttpServletResponse resp) throws IOException {\r
+               setIpAndFqdnForEelf("doDelete");\r
+               eelflogger.info(EelfMsgs.MESSAGE_WITH_BEHALF_AND_FEEDID, req.getHeader(BEHALF_HEADER),getIdFromPath(req)+"");\r
+               redirect(req, resp);\r
+       }\r
+       @Override\r
+       public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {\r
+               setIpAndFqdnForEelf("doGet");\r
+               eelflogger.info(EelfMsgs.MESSAGE_WITH_BEHALF_AND_FEEDID, req.getHeader(BEHALF_HEADER),getIdFromPath(req)+"");\r
+               redirect(req, resp);\r
+       }\r
+       @Override\r
+       public void doPut(HttpServletRequest req, HttpServletResponse resp) throws IOException {\r
+               setIpAndFqdnForEelf("doPut");\r
+               eelflogger.info(EelfMsgs.MESSAGE_WITH_BEHALF_AND_FEEDID, req.getHeader(BEHALF_HEADER),getIdFromPath(req)+"");\r
+               redirect(req, resp);\r
+       }\r
+       @Override\r
+       public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {\r
+               setIpAndFqdnForEelf("doPost");\r
+               eelflogger.info(EelfMsgs.MESSAGE_WITH_BEHALF, req.getHeader(BEHALF_HEADER));\r
+               redirect(req, resp);\r
+       }\r
+       private void redirect(HttpServletRequest req, HttpServletResponse resp) throws IOException {\r
+               String[] nodes = getNodes();\r
+               if (nodes == null || nodes.length == 0) {\r
+                       resp.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE, "There are no nodes defined in the DR network.");\r
+               } else {\r
+                       EventLogRecord elr = new EventLogRecord(req);\r
+                       int feedid = checkPath(req);\r
+                       if (feedid < 0) {\r
+                               String message = (feedid == -1)\r
+                                       ? "Invalid request - Missing or bad feed number."\r
+                                       : "Invalid request - Missing file ID.";\r
+                               elr.setMessage(message);\r
+                               elr.setResult(HttpServletResponse.SC_NOT_FOUND);\r
+                               eventlogger.info(elr);\r
+\r
+                               resp.sendError(HttpServletResponse.SC_NOT_FOUND, message);\r
+                       } else {\r
+                               // Generate new URL\r
+                               String nextnode = getRedirectNode(feedid, req);\r
+                               nextnode = nextnode+":"+DB.HTTPS_PORT;\r
+                               String newurl = "https://" + nextnode + "/publish" + req.getPathInfo();\r
+                               String qs = req.getQueryString();\r
+                               if (qs != null)\r
+                                       newurl += "?" + qs;\r
+\r
+                               // Log redirect in event log\r
+                               String message = "Redirected to: "+newurl;\r
+                               elr.setMessage(message);\r
+                               elr.setResult(HttpServletResponse.SC_MOVED_PERMANENTLY);\r
+                               eventlogger.info(elr);\r
+\r
+                               resp.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY);\r
+                               resp.setHeader("Location", newurl);\r
+                       }\r
+               }\r
+       }\r
+       private String getRedirectNode(int feedid, HttpServletRequest req) {\r
+               // Check to see if the IRT needs to be updated\r
+               Poker p = Poker.getPoker();\r
+               String s = p.getProvisioningString();\r
+               synchronized (provstring) {\r
+                       if (irt == null || (s.length() != provstring.length()) || !s.equals(provstring)) {\r
+                               // Provisioning string has changed -- update the IRT\r
+                               provstring = s;\r
+                               JSONObject jo = new JSONObject(new JSONTokener(provstring));\r
+                               JSONArray ja = jo.getJSONArray("ingress");\r
+                               List<IngressRoute> newlist = new ArrayList<IngressRoute>();\r
+                               for (int i = 0; i < ja.length(); i++) {\r
+                                       IngressRoute iroute = new IngressRoute(ja.getJSONObject(i));\r
+                                       newlist.add(iroute);\r
+                               }\r
+                               irt = newlist;\r
+                       }\r
+               }\r
+\r
+               // Look in IRT for next node\r
+               for (IngressRoute route : irt) {\r
+                       if (route.matches(feedid, req)) {\r
+                               // pick a node at random from the list\r
+                               Collection<String> nodes = route.getNodes();\r
+                               String[] arr = nodes.toArray(new String[0]);\r
+                               long id = System.currentTimeMillis() % arr.length;\r
+                               String node = arr[(int) id];\r
+                               intlogger.info("Redirecting to "+node+" because of route "+route);\r
+                               return node;\r
+                       }\r
+               }\r
+\r
+               // No IRT rule matches, do round robin of all active nodes\r
+               String[] nodes = getNodes();\r
+               if (next_node >= nodes.length)  // The list of nodes may have grown/shrunk\r
+                       next_node = 0;\r
+               return nodes[next_node++];\r
+       }\r
+       private int checkPath(HttpServletRequest req) {\r
+               String path = req.getPathInfo();\r
+               if (path == null || path.length() < 2)\r
+                       return -1;\r
+               path = path.substring(1);\r
+               int ix = path.indexOf('/');\r
+               if (ix < 0 || ix == path.length()-1)\r
+                       return -2;\r
+               try {\r
+                       int feedid = Integer.parseInt(path.substring(0, ix));\r
+                       if (!Feed.isFeedValid(feedid))\r
+                               return -1;\r
+                       return feedid;\r
+               } catch (NumberFormatException e) {\r
+                       return -1;\r
+               }\r
+       }\r
+}\r