Checkstyle fixes for datarouter prov
[dmaap/datarouter.git] / datarouter-prov / src / main / java / org / onap / dmaap / datarouter / provisioning / RouteServlet.java
1 /*******************************************************************************\r
2  * ============LICENSE_START==================================================\r
3  * * org.onap.dmaap\r
4  * * ===========================================================================\r
5  * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.\r
6  * * ===========================================================================\r
7  * * Licensed under the Apache License, Version 2.0 (the "License");\r
8  * * you may not use this file except in compliance with the License.\r
9  * * You may obtain a copy of the License at\r
10  * *\r
11  *  *      http://www.apache.org/licenses/LICENSE-2.0\r
12  * *\r
13  *  * Unless required by applicable law or agreed to in writing, software\r
14  * * distributed under the License is distributed on an "AS IS" BASIS,\r
15  * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
16  * * See the License for the specific language governing permissions and\r
17  * * limitations under the License.\r
18  * * ============LICENSE_END====================================================\r
19  * *\r
20  * * ECOMP is a trademark and service mark of AT&T Intellectual Property.\r
21  * *\r
22  ******************************************************************************/\r
23 \r
24 \r
25 package org.onap.dmaap.datarouter.provisioning;\r
26 \r
27 import static org.onap.dmaap.datarouter.provisioning.utils.HttpServletUtils.sendResponseError;\r
28 \r
29 import java.io.IOException;\r
30 import java.util.Set;\r
31 import javax.servlet.http.HttpServletRequest;\r
32 import javax.servlet.http.HttpServletResponse;\r
33 import org.json.JSONException;\r
34 import org.json.JSONObject;\r
35 \r
36 import org.onap.dmaap.datarouter.provisioning.beans.Deleteable;\r
37 import org.onap.dmaap.datarouter.provisioning.beans.EgressRoute;\r
38 import org.onap.dmaap.datarouter.provisioning.beans.EventLogRecord;\r
39 import org.onap.dmaap.datarouter.provisioning.beans.IngressRoute;\r
40 import org.onap.dmaap.datarouter.provisioning.beans.Insertable;\r
41 import org.onap.dmaap.datarouter.provisioning.beans.NetworkRoute;\r
42 import org.onap.dmaap.datarouter.provisioning.beans.NodeClass;\r
43 \r
44 \r
45 \r
46 /**\r
47  * <p>\r
48  * This servlet handles requests to URLs under /internal/route/ on the provisioning server.\r
49  * This part of the URL tree is used to manipulate the Data Router routing tables.\r
50  * These include:\r
51  * </p>\r
52  * <div class="contentContainer">\r
53  * <table class="packageSummary" border="0" cellpadding="3" cellspacing="0">\r
54  * <caption>\r
55  *     <span>URL Path Summary</span>\r
56  *     <span class="tabEnd">&nbsp;</span>\r
57  * </caption>\r
58  * <tr>\r
59  *   <th class="colFirst" width="35%">URL Path</th>\r
60  *   <th class="colOne">Method</th>\r
61  *   <th class="colLast">Purpose</th>\r
62  * </tr>\r
63  * <tr class="altColor">\r
64  *   <td class="colFirst">/internal/route/</td>\r
65  *   <td class="colOne">GET</td>\r
66  *   <td class="colLast">used to GET a full JSON copy of all three routing tables.</td>\r
67  * </tr>\r
68  * <tr class="rowColor">\r
69  *   <td class="colFirst" rowspan="2">/internal/route/ingress/</td>\r
70  *   <td class="colOne">GET</td>\r
71  *   <td class="colLast">used to GET a full JSON copy of the ingress routing table (IRT).</td>\r
72  * </tr>\r
73  * <tr class="rowColor">\r
74  *   <td class="colOne">POST</td>\r
75  *   <td class="colLast">used to create a new entry in the ingress routing table (IRT).</td></tr>\r
76  * <tr class="altColor">\r
77  *   <td class="colFirst" rowspan="2">/internal/route/egress/</td>\r
78  *   <td class="colOne">GET</td>\r
79  *   <td class="colLast">used to GET a full JSON copy of the egress routing table (ERT).</td>\r
80  * </tr>\r
81  * <tr class="altColor">\r
82  *   <td class="colOne">POST</td>\r
83  *   <td class="colLast">used to create a new entry in the egress routing table (ERT).</td></tr>\r
84  * <tr class="rowColor">\r
85  *   <td class="colFirst" rowspan="2">/internal/route/network/</td>\r
86  *   <td class="colOne">GET</td>\r
87  *   <td class="colLast">used to GET a full JSON copy of the network routing table (NRT).</td>\r
88  * </tr>\r
89  * <tr class="rowColor">\r
90  *   <td class="colOne">POST</td>\r
91  *   <td class="colLast">used to create a new entry in the network routing table (NRT).</td>\r
92  * </tr>\r
93  * <tr class="altColor">\r
94  *   <td class="colFirst">/internal/route/ingress/&lt;feed&gt;/&lt;user&gt;/&lt;subnet&gt;</td>\r
95  *   <td class="colOne">DELETE</td>\r
96  *   <td class="colLast">used to DELETE the ingress route corresponding to <i>feed</i>, <i>user</i> and <i>subnet</i>.\r
97  *   The / in the subnet specified should be replaced with a !, since / cannot be used in a URL.</td>\r
98  * </tr>\r
99  * <tr class="rowColor">\r
100  *   <td class="colFirst">/internal/route/ingress/&lt;seq&gt;</td>\r
101  *   <td class="colOne">DELETE</td>\r
102  *   <td class="colLast">used to DELETE all ingress routes with the matching <i>seq</i> sequence number.</td>\r
103  * </tr>\r
104  * <tr class="altColor">\r
105  *   <td class="colFirst">/internal/route/egress/&lt;sub&gt;</td>\r
106  *   <td class="colOne">DELETE</td>\r
107  *   <td class="colLast">used to DELETE the egress route the matching <i>sub</i> subscriber number.</td>\r
108  * </tr>\r
109  * <tr class="rowColor">\r
110  *   <td class="colFirst">/internal/route/network/&lt;fromnode&gt;/&lt;tonode&gt;</td>\r
111  *   <td class="colOne">DELETE</td>\r
112  *   <td class="colLast">used to DELETE the network route corresponding to <i>fromnode</i>\r
113  *   and <i>tonode</i>.</td>\r
114  * </tr>\r
115  * </table>\r
116  * </div>\r
117  * <p>\r
118  * Authorization to use these URLs is a little different than for other URLs on the provisioning server.\r
119  * For the most part, the IP address that the request comes from should be either:\r
120  * </p>\r
121  * <ol>\r
122  * <li>an IP address of a provisioning server, or</li>\r
123  * <li>the IP address of a node, or</li>\r
124  * <li>an IP address from the "<i>special subnet</i>" which is configured with\r
125  * the PROV_SPECIAL_SUBNET parameter.\r
126  * </ol>\r
127  * <p>\r
128  * All DELETE/GET/POST requests made to this servlet on the standby server are proxied to the\r
129  * active server (using the {@link ProxyServlet}) if it is up and reachable.\r
130  * </p>\r
131  *\r
132  * @author Robert Eby\r
133  * @version $Id$\r
134  */\r
135 @SuppressWarnings("serial")\r
136 \r
137 public class RouteServlet extends ProxyServlet {\r
138 \r
139     /**\r
140      * DELETE route table entries by deleting part of the route table tree.\r
141      */\r
142     @Override\r
143     public void doDelete(HttpServletRequest req, HttpServletResponse resp) {\r
144         EventLogRecord elr = new EventLogRecord(req);\r
145         if (!isAuthorizedForInternal(req)) {\r
146             elr.setMessage(UNAUTHORIZED);\r
147             elr.setResult(HttpServletResponse.SC_FORBIDDEN);\r
148             eventlogger.error(elr.toString());\r
149             sendResponseError(resp, HttpServletResponse.SC_FORBIDDEN, UNAUTHORIZED, eventlogger);\r
150             return;\r
151         }\r
152         if (isProxyOK(req) && isProxyServer()) {\r
153             super.doDelete(req, resp);\r
154             return;\r
155         }\r
156 \r
157         String path = req.getPathInfo();\r
158         String[] parts = path.substring(1).split("/");\r
159         Deleteable[] deleteables = null;\r
160         if ("ingress".equals(parts[0])) {\r
161             if (parts.length == 4) {\r
162                 // /internal/route/ingress/<feed>/<user>/<subnet>\r
163                 try {\r
164                     int feedid = Integer.parseInt(parts[1]);\r
165                     IngressRoute er = IngressRoute.getIngressRoute(feedid, parts[2], parts[3].replaceAll("!", "/"));\r
166                     if (er == null) {\r
167                         sendResponseError(resp,\r
168                             HttpServletResponse.SC_NOT_FOUND, "The specified ingress route does not exist.",\r
169                                 eventlogger);\r
170                         return;\r
171                     }\r
172                     deleteables = new Deleteable[] { er };\r
173                 } catch (NumberFormatException e) {\r
174                     sendResponseError(resp, HttpServletResponse.SC_NOT_FOUND,\r
175                             "Invalid feed ID in 'delete ingress' command.", eventlogger);\r
176                     return;\r
177                 }\r
178             } else if (parts.length == 2) {\r
179                 // /internal/route/ingress/<seq>\r
180                 try {\r
181                     int seq = Integer.parseInt(parts[1]);\r
182                     Set<IngressRoute> set = IngressRoute.getIngressRoutesForSeq(seq);\r
183                     deleteables = set.toArray(new Deleteable[0]);\r
184                 } catch (NumberFormatException e) {\r
185                     sendResponseError(resp, HttpServletResponse.SC_NOT_FOUND,\r
186                             "Invalid sequence number in 'delete ingress' command.", eventlogger);\r
187                     return;\r
188                 }\r
189             } else {\r
190                 sendResponseError(resp, HttpServletResponse.SC_NOT_FOUND,\r
191                         "Invalid number of arguments in 'delete ingress' command.", eventlogger);\r
192                 return;\r
193             }\r
194         } else if ("egress".equals(parts[0])) {\r
195             if (parts.length == 2) {\r
196                 // /internal/route/egress/<sub>\r
197                 try {\r
198                     int subid = Integer.parseInt(parts[1]);\r
199                     EgressRoute er = EgressRoute.getEgressRoute(subid);\r
200                     if (er == null) {\r
201                         sendResponseError(resp, HttpServletResponse.SC_NOT_FOUND,\r
202                                 "The specified egress route does not exist.", eventlogger);\r
203                         return;\r
204                     }\r
205                     deleteables = new Deleteable[] { er };\r
206                 } catch (NumberFormatException e) {\r
207                     sendResponseError(resp, HttpServletResponse.SC_NOT_FOUND,\r
208                             "Invalid sub ID in 'delete egress' command.", eventlogger);\r
209                     return;\r
210                 }\r
211             } else {\r
212                 sendResponseError(resp, HttpServletResponse.SC_NOT_FOUND,\r
213                         "Invalid number of arguments in 'delete egress' command.", eventlogger);\r
214                 return;\r
215             }\r
216         } else if ("network".equals(parts[0])) {\r
217             if (parts.length == 3) {\r
218                 // /internal/route/network/<from>/<to>\r
219                 try {\r
220                     NetworkRoute nr = new NetworkRoute(\r
221                         NodeClass.normalizeNodename(parts[1]),\r
222                         NodeClass.normalizeNodename(parts[2])\r
223                     );\r
224                     deleteables = new Deleteable[] { nr };\r
225                 } catch (IllegalArgumentException e) {\r
226                     String message = "The specified network route does not exist.";\r
227                     eventlogger.error(message, e);\r
228                     sendResponseError(resp, HttpServletResponse.SC_NOT_FOUND, message, eventlogger);\r
229                     return;\r
230                 }\r
231             } else {\r
232                 sendResponseError(resp, HttpServletResponse.SC_NOT_FOUND,\r
233                         "Invalid number of arguments in 'delete network' command.", eventlogger);\r
234                 return;\r
235             }\r
236         }\r
237         if (deleteables == null) {\r
238             sendResponseError(resp, HttpServletResponse.SC_NOT_FOUND, BAD_URL, eventlogger);\r
239             return;\r
240         }\r
241         boolean rv = true;\r
242         for (Deleteable dd : deleteables) {\r
243             rv &= doDelete(dd);\r
244         }\r
245         if (rv) {\r
246             elr.setResult(HttpServletResponse.SC_OK);\r
247             eventlogger.info(elr.toString());\r
248             resp.setStatus(HttpServletResponse.SC_OK);\r
249             provisioningDataChanged();\r
250             provisioningParametersChanged();\r
251         } else {\r
252             // Something went wrong with the DELETE\r
253             elr.setResult(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);\r
254             eventlogger.error(elr.toString());\r
255             sendResponseError(resp, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, DB_PROBLEM_MSG, eventlogger);\r
256         }\r
257     }\r
258 \r
259     /**\r
260      * GET route table entries from the route table tree specified by the URL path.\r
261      */\r
262     @Override\r
263     public void doGet(HttpServletRequest req, HttpServletResponse resp) {\r
264         EventLogRecord elr = new EventLogRecord(req);\r
265         if (!isAuthorizedForInternal(req)) {\r
266             elr.setMessage(UNAUTHORIZED);\r
267             elr.setResult(HttpServletResponse.SC_FORBIDDEN);\r
268             eventlogger.error(elr.toString());\r
269             sendResponseError(resp, HttpServletResponse.SC_FORBIDDEN, UNAUTHORIZED, eventlogger);\r
270             return;\r
271         }\r
272         if (isProxyOK(req) && isProxyServer()) {\r
273             super.doGet(req, resp);\r
274             return;\r
275         }\r
276 \r
277         String path = req.getPathInfo();\r
278         if (!path.endsWith("/")) {\r
279             path += "/";\r
280         }\r
281         if (!"/".equals(path) && !INGRESS.equals(path) && !EGRESS.equals(path) && !NETWORK.equals(path)) {\r
282             sendResponseError(resp, HttpServletResponse.SC_NOT_FOUND, BAD_URL, eventlogger);\r
283             return;\r
284         }\r
285 \r
286         StringBuilder sb = new StringBuilder("{\n");\r
287         String px2 = "";\r
288         if ("/".equals(path) || INGRESS.equals(path)) {\r
289             String pfx = "\n";\r
290             sb.append("\"ingress\": [");\r
291             for (IngressRoute in : IngressRoute.getAllIngressRoutes()) {\r
292                 sb.append(pfx);\r
293                 sb.append(in.asJSONObject().toString());\r
294                 pfx = ",\n";\r
295             }\r
296             sb.append("\n]");\r
297             px2 = ",\n";\r
298         }\r
299 \r
300         if ("/".equals(path) || EGRESS.equals(path)) {\r
301             String pfx = "\n";\r
302             sb.append(px2);\r
303             sb.append("\"egress\": {");\r
304             for (EgressRoute eg : EgressRoute.getAllEgressRoutes()) {\r
305                 JSONObject jx = eg.asJSONObject();\r
306                 for (String key : jx.keySet()) {\r
307                     sb.append(pfx);\r
308                     sb.append("  \"").append(key).append("\": ");\r
309                     try {\r
310                         sb.append("\"").append(jx.getString(key)).append("\"");\r
311                     } catch (JSONException je) {\r
312                         eventlogger.error("PROV0161 RouteServlet.doGet: " + je.getMessage(), je);\r
313                     }\r
314                     pfx = ",\n";\r
315                 }\r
316             }\r
317             sb.append("\n}");\r
318             px2 = ",\n";\r
319         }\r
320 \r
321         if ("/".equals(path) || NETWORK.equals(path)) {\r
322             String pfx = "\n";\r
323             sb.append(px2);\r
324             sb.append("\"routing\": [");\r
325             for (NetworkRoute ne : NetworkRoute.getAllNetworkRoutes()) {\r
326                 sb.append(pfx);\r
327                 sb.append(ne.asJSONObject().toString());\r
328                 pfx = ",\n";\r
329             }\r
330             sb.append("\n]");\r
331         }\r
332         sb.append("}\n");\r
333         resp.setStatus(HttpServletResponse.SC_OK);\r
334         resp.setContentType("application/json");\r
335         try {\r
336             resp.getOutputStream().print(sb.toString());\r
337         } catch (IOException ioe) {\r
338             eventlogger.error("PROV0162 RouteServlet.doGet: " + ioe.getMessage(), ioe);\r
339         }\r
340     }\r
341 \r
342     /**\r
343      * PUT on &lt;/internal/route/*&gt; -- not supported.\r
344      */\r
345     @Override\r
346     public void doPut(HttpServletRequest req, HttpServletResponse resp) {\r
347         EventLogRecord elr = new EventLogRecord(req);\r
348         if (!isAuthorizedForInternal(req)) {\r
349             elr.setMessage(UNAUTHORIZED);\r
350             elr.setResult(HttpServletResponse.SC_FORBIDDEN);\r
351             eventlogger.error(elr.toString());\r
352             sendResponseError(resp, HttpServletResponse.SC_FORBIDDEN, UNAUTHORIZED, eventlogger);\r
353             return;\r
354         }\r
355         sendResponseError(resp, HttpServletResponse.SC_NOT_FOUND, BAD_URL, eventlogger);\r
356     }\r
357 \r
358     /**\r
359      * POST - modify existing route table entries in the route table tree specified by the URL path.\r
360      */\r
361     @Override\r
362     public void doPost(HttpServletRequest req, HttpServletResponse resp) {\r
363         EventLogRecord elr = new EventLogRecord(req);\r
364         if (!isAuthorizedForInternal(req)) {\r
365             elr.setMessage(UNAUTHORIZED);\r
366             elr.setResult(HttpServletResponse.SC_FORBIDDEN);\r
367             eventlogger.error(elr.toString());\r
368             sendResponseError(resp, HttpServletResponse.SC_FORBIDDEN, UNAUTHORIZED, eventlogger);\r
369             return;\r
370         }\r
371         if (isProxyOK(req) && isProxyServer()) {\r
372             super.doPost(req, resp);\r
373             return;\r
374         }\r
375         String path = req.getPathInfo();\r
376         Insertable[] ins = null;\r
377         if (path.startsWith(INGRESS)) {\r
378             // /internal/route/ingress/?feed=%s&amp;user=%s&amp;subnet=%s&amp;nodepatt=%s\r
379             try {\r
380                 // Although it probably doesn't make sense, you can install two identical routes in the IRT\r
381                 int feedid = Integer.parseInt(req.getParameter("feed"));\r
382                 String user = req.getParameter("user");\r
383                 if (user == null) {\r
384                     user = "-";\r
385                 }\r
386                 String subnet = req.getParameter("subnet");\r
387                 if (subnet == null) {\r
388                     subnet = "-";\r
389                 }\r
390                 String nodepatt = req.getParameter("nodepatt");\r
391                 String str = req.getParameter("seq");\r
392                 int seq = (str != null) ? Integer.parseInt(str) : (IngressRoute.getMaxSequence() + 100);\r
393                 ins = new Insertable[] { new IngressRoute(seq, feedid,\r
394                         user, subnet, NodeClass.lookupNodeNames(nodepatt)) };\r
395             } catch (Exception e) {\r
396                 intlogger.info(e.toString(), e);\r
397                 sendResponseError(resp, HttpServletResponse.SC_BAD_REQUEST,\r
398                         "Invalid arguments in 'add ingress' command.", intlogger);\r
399                 return;\r
400             }\r
401         } else if (path.startsWith(EGRESS)) {\r
402             // /internal/route/egress/?sub=%s&amp;node=%s\r
403             try {\r
404                 int subid = Integer.parseInt(req.getParameter("sub"));\r
405                 EgressRoute er = EgressRoute.getEgressRoute(subid);\r
406                 if (er != null) {\r
407                     sendResponseError(resp, HttpServletResponse.SC_BAD_REQUEST,\r
408                             "An egress route already exists for that subscriber.", intlogger);\r
409                     return;\r
410                 }\r
411                 String node = NodeClass.normalizeNodename(req.getParameter("node"));\r
412                 ins = new Insertable[] { new EgressRoute(subid, node) };\r
413             } catch (Exception e) {\r
414                 intlogger.info(e.toString(), e);\r
415                 sendResponseError(resp, HttpServletResponse.SC_BAD_REQUEST,\r
416                         "Invalid arguments in 'add egress' command.", intlogger);\r
417                 return;\r
418             }\r
419         } else if (path.startsWith(NETWORK)) {\r
420             // /internal/route/network/?from=%s&amp;to=%s&amp;via=%s\r
421             try {\r
422                 String nfrom = req.getParameter("from");\r
423                 String nto   = req.getParameter("to");\r
424                 String nvia  = req.getParameter("via");\r
425                 if (nfrom == null || nto == null || nvia == null) {\r
426                     sendResponseError(resp, HttpServletResponse.SC_BAD_REQUEST,\r
427                             "Missing arguments in 'add network' command.", intlogger);\r
428                     return;\r
429                 }\r
430                 nfrom = NodeClass.normalizeNodename(nfrom);\r
431                 nto   = NodeClass.normalizeNodename(nto);\r
432                 nvia  = NodeClass.normalizeNodename(nvia);\r
433                 NetworkRoute nr = new NetworkRoute(nfrom, nto, nvia);\r
434                 for (NetworkRoute route : NetworkRoute.getAllNetworkRoutes()) {\r
435                     if (route.getFromnode() == nr.getFromnode() && route.getTonode() == nr.getTonode()) {\r
436                         sendResponseError(resp, HttpServletResponse.SC_BAD_REQUEST,\r
437                                 "Network route table already contains a route for " + nfrom\r
438                                         + " and " + nto, intlogger);\r
439                         return;\r
440                     }\r
441                 }\r
442                 ins = new Insertable[] { nr };\r
443             } catch (IllegalArgumentException e) {\r
444                 intlogger.info(e.toString(), e);\r
445                 sendResponseError(resp, HttpServletResponse.SC_BAD_REQUEST,\r
446                         "Invalid arguments in 'add network' command.", intlogger);\r
447                 return;\r
448             }\r
449         }\r
450         if (ins == null) {\r
451             sendResponseError(resp, HttpServletResponse.SC_NOT_FOUND, BAD_URL, intlogger);\r
452             return;\r
453         }\r
454         boolean rv = true;\r
455         for (Insertable dd : ins) {\r
456             rv &= doInsert(dd);\r
457         }\r
458         if (rv) {\r
459             elr.setResult(HttpServletResponse.SC_OK);\r
460             eventlogger.info(elr.toString());\r
461             resp.setStatus(HttpServletResponse.SC_OK);\r
462             provisioningDataChanged();\r
463             provisioningParametersChanged();\r
464         } else {\r
465             // Something went wrong with the INSERT\r
466             elr.setResult(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);\r
467             eventlogger.error(elr.toString());\r
468             sendResponseError(resp, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, DB_PROBLEM_MSG, intlogger);\r
469         }\r
470     }\r
471 }\r