Remove major and minor code smells in dr-node
[dmaap/datarouter.git] / datarouter-node / src / main / java / org / onap / dmaap / datarouter / node / NodeConfig.java
index c196d46..7f01821 100644 (file)
-/*******************************************************************************\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 org.onap.dmaap.datarouter.node;\r
-\r
-import java.util.*;\r
-import java.io.*;\r
-\r
-/**\r
- *     Processed configuration for this node.\r
- *     <p>\r
- *     The NodeConfig represents a processed configuration from the Data Router\r
- *     provisioning server.  Each time configuration data is received from the\r
- *     provisioning server, a new NodeConfig is created and the previous one\r
- *     discarded.\r
- */\r
-public class NodeConfig        {\r
-       /**\r
-        *      Raw configuration entry for a data router node\r
-        */\r
-       public static class ProvNode {\r
-               private String cname;\r
-               /**\r
-                *      Construct a node configuration entry.\r
-                *      @param cname    The cname of the node.\r
-                */\r
-               public ProvNode(String cname) {\r
-                       this.cname = cname;\r
-               }\r
-               /**\r
-                *      Get the cname of the node\r
-                */\r
-               public String getCName() {\r
-                       return(cname);\r
-               }\r
-       }\r
-       /**\r
-        *      Raw configuration entry for a provisioning parameter\r
-        */\r
-       public static class ProvParam {\r
-               private String name;\r
-               private String value;\r
-               /**\r
-                *      Construct a provisioning parameter configuration entry.\r
-                *      @param  name The name of the parameter.\r
-                *      @param  value The value of the parameter.\r
-                */\r
-               public ProvParam(String name, String value) {\r
-                       this.name = name;\r
-                       this.value = value;\r
-               }\r
-               /**\r
-                *      Get the name of the parameter.\r
-                */\r
-               public String getName() {\r
-                       return(name);\r
-               }\r
-               /**\r
-                *      Get the value of the parameter.\r
-                */\r
-               public String getValue() {\r
-                       return(value);\r
-               }\r
-       }\r
-       /**\r
-        *      Raw configuration entry for a data feed.\r
-        */\r
-       public static class ProvFeed {\r
-               private String id;\r
-               private String logdata;\r
-               private String status;\r
-               /**\r
-                *      Construct a feed configuration entry.\r
-                *      @param id       The feed ID of the entry.\r
-                *      @param logdata  String for log entries about the entry.\r
-                *      @param status   The reason why this feed cannot be used (Feed has been deleted, Feed has been suspended) or null if it is valid.\r
-                */\r
-               public ProvFeed(String id, String logdata, String status) {\r
-                       this.id = id;\r
-                       this.logdata = logdata;\r
-                       this.status = status;\r
-               }\r
-               /**\r
-                *      Get the feed id of the data feed.\r
-                */\r
-               public String getId() {\r
-                       return(id);\r
-               }\r
-               /**\r
-                *      Get the log data of the data feed.\r
-                */\r
-               public String getLogData() {\r
-                       return(logdata);\r
-               }\r
-               /**\r
-                *      Get the status of the data feed.\r
-                */\r
-               public String getStatus() {\r
-                       return(status);\r
-               }\r
-       }\r
-       /**\r
-        *      Raw configuration entry for a feed user.\r
-        */\r
-       public static class ProvFeedUser        {\r
-               private String feedid;\r
-               private String user;\r
-               private String credentials;\r
-               /**\r
-                *      Construct a feed user configuration entry\r
-                *      @param feedid   The feed id.\r
-                *      @param user     The user that will publish to the feed.\r
-                *      @param credentials      The Authorization header the user will use to publish.\r
-                */\r
-               public ProvFeedUser(String feedid, String user, String credentials) {\r
-                       this.feedid = feedid;\r
-                       this.user = user;\r
-                       this.credentials = credentials;\r
-               }\r
-               /**\r
-                *      Get the feed id of the feed user.\r
-                */\r
-               public String getFeedId() {\r
-                       return(feedid);\r
-               }\r
-               /**\r
-                *      Get the user for the feed user.\r
-                */\r
-               public String getUser() {\r
-                       return(user);\r
-               }\r
-               /**\r
-                *      Get the credentials for the feed user.\r
-                */\r
-               public String getCredentials() {\r
-                       return(credentials);\r
-               }\r
-       }\r
-       /**\r
-        *      Raw configuration entry for a feed subnet\r
-        */\r
-       public static class ProvFeedSubnet      {\r
-               private String feedid;\r
-               private String cidr;\r
-               /**\r
-                *      Construct a feed subnet configuration entry\r
-                *      @param feedid   The feed ID\r
-                *      @param cidr     The CIDR allowed to publish to the feed.\r
-                */\r
-               public ProvFeedSubnet(String feedid, String cidr) {\r
-                       this.feedid = feedid;\r
-                       this.cidr = cidr;\r
-               }\r
-               /**\r
-                *      Get the feed id of the feed subnet.\r
-                */\r
-               public String getFeedId() {\r
-                       return(feedid);\r
-               }\r
-               /**\r
-                *      Get the CIDR of the feed subnet.\r
-                */\r
-               public String getCidr() {\r
-                       return(cidr);\r
-               }\r
-       }\r
-       /**\r
-        *      Raw configuration entry for a subscription\r
-        */\r
-       public static class ProvSubscription    {\r
-               private String  subid;\r
-               private String  feedid;\r
-               private String  url;\r
-               private String  authuser;\r
-               private String  credentials;\r
-               private boolean metaonly;\r
-               private boolean use100;\r
-               /**\r
-                *      Construct a subscription configuration entry\r
-                *      @param subid    The subscription ID\r
-                *      @param feedid   The feed ID\r
-                *      @param url      The base delivery URL (not including the fileid)\r
-                *      @param authuser The user in the credentials used to deliver\r
-                *      @param credentials      The credentials used to authenticate to the delivery URL exactly as they go in the Authorization header.\r
-                *      @param metaonly Is this a meta data only subscription?\r
-                *      @param use100   Should we send Expect: 100-continue?\r
-                */\r
-               public ProvSubscription(String subid, String feedid, String url, String authuser, String credentials, boolean metaonly, boolean use100) {\r
-                       this.subid = subid;\r
-                       this.feedid = feedid;\r
-                       this.url = url;\r
-                       this.authuser = authuser;\r
-                       this.credentials = credentials;\r
-                       this.metaonly = metaonly;\r
-                       this.use100 = use100;\r
-               }\r
-               /**\r
-                *      Get the subscription ID\r
-                */\r
-               public String getSubId() {\r
-                       return(subid);\r
-               }\r
-               /**\r
-                *      Get the feed ID\r
-                */\r
-               public String getFeedId() {\r
-                       return(feedid);\r
-               }\r
-               /**\r
-                *      Get the delivery URL\r
-                */\r
-               public String getURL() {\r
-                       return(url);\r
-               }\r
-               /**\r
-                *      Get the user\r
-                */\r
-               public String getAuthUser() {\r
-                       return(authuser);\r
-               }\r
-               /**\r
-                *      Get the delivery credentials\r
-                */\r
-               public String getCredentials() {\r
-                       return(credentials);\r
-               }\r
-               /**\r
-                *      Is this a meta data only subscription?\r
-                */\r
-               public boolean isMetaDataOnly() {\r
-                       return(metaonly);\r
-               }\r
-               /**\r
-                *      Should we send Expect: 100-continue?\r
-                */\r
-               public boolean isUsing100() {\r
-                       return(use100);\r
-               }\r
-       }\r
-       /**\r
-        *      Raw configuration entry for controlled ingress to the data router node\r
-        */\r
-       public static class ProvForceIngress    {\r
-               private String feedid;\r
-               private String subnet;\r
-               private String user;\r
-               private String[] nodes;\r
-               /**\r
-                *      Construct a forced ingress configuration entry\r
-                *      @param feedid   The feed ID that this entry applies to\r
-                *      @param subnet   The CIDR for which publisher IP addresses this entry applies to or "" if it applies to all publisher IP addresses\r
-                *      @param user     The publishing user this entry applies to or "" if it applies to all publishing users.\r
-                *      @param nodes    The array of FQDNs of the data router nodes to redirect publication attempts to.\r
-                */\r
-               public ProvForceIngress(String feedid, String subnet, String user, String[] nodes) {\r
-                       this.feedid = feedid;\r
-                       this.subnet = subnet;\r
-                       this.user = user;\r
-                       this.nodes = nodes;\r
-               }\r
-               /**\r
-                *      Get the feed ID\r
-                */\r
-               public String getFeedId() {\r
-                       return(feedid);\r
-               }\r
-               /**\r
-                *      Get the subnet\r
-                */\r
-               public String getSubnet() {\r
-                       return(subnet);\r
-               }\r
-               /**\r
-                *      Get the user\r
-                */\r
-               public String getUser() {\r
-                       return(user);\r
-               }\r
-               /**\r
-                *      Get the node\r
-                */\r
-               public String[] getNodes() {\r
-                       return(nodes);\r
-               }\r
-       }\r
-       /**\r
-        *      Raw configuration entry for controlled egress from the data router\r
-        */\r
-       public static class ProvForceEgress     {\r
-               private String subid;\r
-               private String node;\r
-               /**\r
-                *      Construct a forced egress configuration entry\r
-                *      @param subid    The subscription ID the subscription with forced egress\r
-                *      @param node     The node handling deliveries for this subscription\r
-                */\r
-               public ProvForceEgress(String subid, String node) {\r
-                       this.subid = subid;\r
-                       this.node = node;\r
-               }\r
-               /**\r
-                *      Get the subscription ID\r
-                */\r
-               public String getSubId() {\r
-                       return(subid);\r
-               }\r
-               /**\r
-                *      Get the node\r
-                */\r
-               public String getNode() {\r
-                       return(node);\r
-               }\r
-       }\r
-       /**\r
-        *      Raw configuration entry for routing within the data router network\r
-        */\r
-       public static class ProvHop     {\r
-               private String  from;\r
-               private String  to;\r
-               private String  via;\r
-               /**\r
-                *      A human readable description of this entry\r
-                */\r
-               public String toString() {\r
-                       return("Hop " + from + "->" + to + " via " + via);\r
-               }\r
-               /**\r
-                *      Construct a hop entry\r
-                *      @param from     The FQDN of the node with the data to be delivered\r
-                *      @param to       The FQDN of the node that will deliver to the subscriber\r
-                *      @param via      The FQDN of the node where the from node should send the data\r
-                */\r
-               public ProvHop(String from, String to, String via) {\r
-                       this.from = from;\r
-                       this.to = to;\r
-                       this.via = via;\r
-               }\r
-               /**\r
-                *      Get the from node\r
-                */\r
-               public String getFrom() {\r
-                       return(from);\r
-               }\r
-               /**\r
-                *      Get the to node\r
-                */\r
-               public String getTo() {\r
-                       return(to);\r
-               }\r
-               /**\r
-                *      Get the next intermediate node\r
-                */\r
-               public String getVia() {\r
-                       return(via);\r
-               }\r
-       }\r
-       private static class Redirection        {\r
-               public SubnetMatcher snm;\r
-               public String user;\r
-               public String[] nodes;\r
-       }\r
-       private static class Feed       {\r
-               public String   loginfo;\r
-               public String   status;\r
-               public SubnetMatcher[] subnets;\r
-               public Hashtable<String, String> authusers = new Hashtable<String, String>();\r
-               public Redirection[]    redirections;\r
-               public Target[] targets;\r
-       }\r
-       private Hashtable<String, String> params = new Hashtable<String, String>();\r
-       private Hashtable<String, Feed> feeds = new Hashtable<String, Feed>();\r
-       private Hashtable<String, DestInfo> nodeinfo = new Hashtable<String, DestInfo>();\r
-       private Hashtable<String, DestInfo> subinfo = new Hashtable<String, DestInfo>();\r
-       private Hashtable<String, IsFrom> nodes = new Hashtable<String, IsFrom>();\r
-       private String  myname;\r
-       private String  myauth;\r
-       private DestInfo[]      alldests;\r
-       private int     rrcntr;\r
-       /**\r
-        *      Process the raw provisioning data to configure this node\r
-        *      @param pd       The parsed provisioning data\r
-        *      @param myname   My name as seen by external systems\r
-        *      @param spooldir The directory where temporary files live\r
-        *      @param port     The port number for URLs\r
-        *      @param nodeauthkey      The keying string used to generate node authentication credentials\r
-        */\r
-       public NodeConfig(ProvData pd, String myname, String spooldir, int port, String nodeauthkey) {\r
-               this.myname = myname;\r
-               for (ProvParam p: pd.getParams()) {\r
-                       params.put(p.getName(), p.getValue());\r
-               }\r
-               Vector<DestInfo>        div = new Vector<DestInfo>();\r
-               myauth = NodeUtils.getNodeAuthHdr(myname, nodeauthkey);\r
-               for (ProvNode pn: pd.getNodes()) {\r
-                       String cn = pn.getCName();\r
-                       if (nodeinfo.get(cn) != null) {\r
-                               continue;\r
-                       }\r
-                       String auth = NodeUtils.getNodeAuthHdr(cn, nodeauthkey);\r
-                       DestInfo di = new DestInfo("n:" + cn, spooldir + "/n/" + cn, null, "n2n-" + cn, "https://" + cn + ":" + port + "/internal/publish", cn, myauth, false, true);\r
-                       (new File(di.getSpool())).mkdirs();\r
-                       div.add(di);\r
-                       nodeinfo.put(cn, di);\r
-                       nodes.put(auth, new IsFrom(cn));\r
-               }\r
-               PathFinder pf = new PathFinder(myname, nodeinfo.keySet().toArray(new String[nodeinfo.size()]), pd.getHops());\r
-               Hashtable<String, Vector<Redirection>> rdtab = new Hashtable<String, Vector<Redirection>>();\r
-               for (ProvForceIngress pfi: pd.getForceIngress()) {\r
-                       Vector<Redirection> v = rdtab.get(pfi.getFeedId());\r
-                       if (v == null) {\r
-                               v = new Vector<Redirection>();\r
-                               rdtab.put(pfi.getFeedId(), v);\r
-                       }\r
-                       Redirection r = new Redirection();\r
-                       if (pfi.getSubnet() != null) {\r
-                               r.snm = new SubnetMatcher(pfi.getSubnet());\r
-                       }\r
-                       r.user = pfi.getUser();\r
-                       r.nodes = pfi.getNodes();\r
-                       v.add(r);\r
-               }\r
-               Hashtable<String, Hashtable<String, String>> pfutab = new Hashtable<String, Hashtable<String, String>>();\r
-               for (ProvFeedUser pfu: pd.getFeedUsers()) {\r
-                       Hashtable<String, String> t = pfutab.get(pfu.getFeedId());\r
-                       if (t == null) {\r
-                               t = new Hashtable<String, String>();\r
-                               pfutab.put(pfu.getFeedId(), t);\r
-                       }\r
-                       t.put(pfu.getCredentials(), pfu.getUser());\r
-               }\r
-               Hashtable<String, String> egrtab = new Hashtable<String, String>();\r
-               for (ProvForceEgress pfe: pd.getForceEgress()) {\r
-                       if (pfe.getNode().equals(myname) || nodeinfo.get(pfe.getNode()) == null) {\r
-                               continue;\r
-                       }\r
-                       egrtab.put(pfe.getSubId(), pfe.getNode());\r
-               }\r
-               Hashtable<String, Vector<SubnetMatcher>> pfstab = new Hashtable<String, Vector<SubnetMatcher>>();\r
-               for (ProvFeedSubnet pfs: pd.getFeedSubnets()) {\r
-                       Vector<SubnetMatcher> v = pfstab.get(pfs.getFeedId());\r
-                       if (v == null) {\r
-                               v = new Vector<SubnetMatcher>();\r
-                               pfstab.put(pfs.getFeedId(), v);\r
-                       }\r
-                       v.add(new SubnetMatcher(pfs.getCidr()));\r
-               }\r
-               Hashtable<String, StringBuffer> ttab = new Hashtable<String, StringBuffer>();\r
-               HashSet<String> allfeeds = new HashSet<String>();\r
-               for (ProvFeed pfx: pd.getFeeds()) {\r
-                       if (pfx.getStatus() == null) {\r
-                               allfeeds.add(pfx.getId());\r
-                       }\r
-               }\r
-               for (ProvSubscription ps: pd.getSubscriptions()) {\r
-                       String sid = ps.getSubId();\r
-                       String fid = ps.getFeedId();\r
-                       if (!allfeeds.contains(fid)) {\r
-                               continue;\r
-                       }\r
-                       if (subinfo.get(sid) != null) {\r
-                               continue;\r
-                       }\r
-                       int sididx = 999;\r
-                       try {\r
-                               sididx = Integer.parseInt(sid);\r
-                               sididx -= sididx % 100;\r
-                       } catch (Exception e) {\r
-                       }\r
-                       String siddir = sididx + "/" + sid;\r
-                       DestInfo di = new DestInfo("s:" + sid, spooldir + "/s/" + siddir, sid, fid, ps.getURL(), ps.getAuthUser(), ps.getCredentials(), ps.isMetaDataOnly(), ps.isUsing100());\r
-                       (new File(di.getSpool())).mkdirs();\r
-                       div.add(di);\r
-                       subinfo.put(sid, di);\r
-                       String egr = egrtab.get(sid);\r
-                       if (egr != null) {\r
-                               sid = pf.getPath(egr) + sid;\r
-                       }\r
-                       StringBuffer sb = ttab.get(fid);\r
-                       if (sb == null) {\r
-                               sb = new StringBuffer();\r
-                               ttab.put(fid, sb);\r
-                       }\r
-                       sb.append(' ').append(sid);\r
-               }\r
-               alldests = div.toArray(new DestInfo[div.size()]);\r
-               for (ProvFeed pfx: pd.getFeeds()) {\r
-                       String fid = pfx.getId();\r
-                       Feed f = feeds.get(fid);\r
-                       if (f != null) {\r
-                               continue;\r
-                       }\r
-                       f = new Feed();\r
-                       feeds.put(fid, f);\r
-                       f.loginfo = pfx.getLogData();\r
-                       f.status = pfx.getStatus();\r
-                       Vector<SubnetMatcher> v1 = pfstab.get(fid);\r
-                       if (v1 == null) {\r
-                               f.subnets = new SubnetMatcher[0];\r
-                       } else {\r
-                               f.subnets = v1.toArray(new SubnetMatcher[v1.size()]);\r
-                       }\r
-                       Hashtable<String, String> h1 = pfutab.get(fid);\r
-                       if (h1 == null) {\r
-                               h1 = new Hashtable<String, String>();\r
-                       }\r
-                       f.authusers = h1;\r
-                       Vector<Redirection> v2 = rdtab.get(fid);\r
-                       if (v2 == null) {\r
-                               f.redirections = new Redirection[0];\r
-                       } else {\r
-                               f.redirections = v2.toArray(new Redirection[v2.size()]);\r
-                       }\r
-                       StringBuffer sb = ttab.get(fid);\r
-                       if (sb == null) {\r
-                               f.targets = new Target[0];\r
-                       } else {\r
-                               f.targets = parseRouting(sb.toString());\r
-                       }\r
-               }\r
-       }\r
-       /**\r
-        *      Parse a target string into an array of targets\r
-        *      @param routing Target string\r
-        *      @return Array of targets.\r
-        */\r
-       public Target[] parseRouting(String routing) {\r
-               routing = routing.trim();\r
-               if ("".equals(routing)) {\r
-                       return(new Target[0]);\r
-               }\r
-               String[] xx = routing.split("\\s+");\r
-               Hashtable<String, Target> tmap = new Hashtable<String, Target>();\r
-               HashSet<String> subset = new HashSet<String>();\r
-               Vector<Target> tv = new Vector<Target>();\r
-               Target[] ret = new Target[xx.length];\r
-               for (int i = 0; i < xx.length; i++) {\r
-                       String t = xx[i];\r
-                       int j = t.indexOf('/');\r
-                       if (j == -1) {\r
-                               DestInfo di = subinfo.get(t);\r
-                               if (di == null) {\r
-                                       tv.add(new Target(null, t));\r
-                               } else {\r
-                                       if (!subset.contains(t)) {\r
-                                               subset.add(t);\r
-                                               tv.add(new Target(di, null));\r
-                                       }\r
-                               }\r
-                       } else {\r
-                               String node = t.substring(0, j);\r
-                               String rtg = t.substring(j + 1);\r
-                               DestInfo di = nodeinfo.get(node);\r
-                               if (di == null) {\r
-                                       tv.add(new Target(null, t));\r
-                               } else {\r
-                                       Target tt = tmap.get(node);\r
-                                       if (tt == null) {\r
-                                               tt = new Target(di, rtg);\r
-                                               tmap.put(node, tt);\r
-                                               tv.add(tt);\r
-                                       } else {\r
-                                               tt.addRouting(rtg);\r
-                                       }\r
-                               }\r
-                       }\r
-               }\r
-               return(tv.toArray(new Target[tv.size()]));\r
-       }\r
-       /**\r
-        *      Check whether this is a valid node-to-node transfer\r
-        *      @param credentials      Credentials offered by the supposed node\r
-        *      @param ip       IP address the request came from\r
-        */\r
-       public boolean isAnotherNode(String credentials, String ip) {\r
-               IsFrom n = nodes.get(credentials);\r
-               return (n != null && n.isFrom(ip));\r
-       }\r
-       /**\r
-        *      Check whether publication is allowed.\r
-        *      @param feedid   The ID of the feed being requested.\r
-        *      @param credentials      The offered credentials\r
-        *      @param ip       The requesting IP address\r
-        */\r
-       public String isPublishPermitted(String feedid, String credentials, String ip) {\r
-               Feed f = feeds.get(feedid);\r
-               String nf = "Feed does not exist";\r
-               if (f != null) {\r
-                       nf = f.status;\r
-               }\r
-               if (nf != null) {\r
-                       return(nf);\r
-               }\r
-               String user = f.authusers.get(credentials);\r
-               if (user == null) {\r
-                       return("Publisher not permitted for this feed");\r
-               }\r
-               if (f.subnets.length == 0) {\r
-                       return(null);\r
-               }\r
-               byte[] addr = NodeUtils.getInetAddress(ip);\r
-               for (SubnetMatcher snm: f.subnets) {\r
-                       if (snm.matches(addr)) {\r
-                               return(null);\r
-                       }\r
-               }\r
-               return("Publisher not permitted for this feed");\r
-       }\r
-       /**\r
-        *      Get authenticated user\r
-        */\r
-       public String getAuthUser(String feedid, String credentials) {\r
-               return(feeds.get(feedid).authusers.get(credentials));\r
-       }\r
-       /**\r
-        *      Check if the request should be redirected to a different ingress node\r
-        */\r
-       public String getIngressNode(String feedid, String user, String ip) {\r
-               Feed f = feeds.get(feedid);\r
-               if (f.redirections.length == 0) {\r
-                       return(null);\r
-               }\r
-               byte[] addr = NodeUtils.getInetAddress(ip);\r
-               for (Redirection r: f.redirections) {\r
-                       if (r.user != null && !user.equals(r.user)) {\r
-                               continue;\r
-                       }\r
-                       if (r.snm != null && !r.snm.matches(addr)) {\r
-                               continue;\r
-                       }\r
-                       for (String n: r.nodes) {\r
-                               if (myname.equals(n)) {\r
-                                       return(null);\r
-                               }\r
-                       }\r
-                       if (r.nodes.length == 0) {\r
-                               return(null);\r
-                       }\r
-                       return(r.nodes[rrcntr++ % r.nodes.length]);\r
-               }\r
-               return(null);\r
-       }\r
-       /**\r
-        *      Get a provisioned configuration parameter\r
-        */\r
-       public String getProvParam(String name) {\r
-               return(params.get(name));\r
-       }\r
-       /**\r
-        *      Get all the DestInfos\r
-        */\r
-       public DestInfo[]       getAllDests() {\r
-               return(alldests);\r
-       }\r
-       /**\r
-        *      Get the targets for a feed\r
-        *      @param feedid   The feed ID\r
-        *      @return The targets this feed should be delivered to\r
-        */\r
-       public Target[] getTargets(String feedid) {\r
-               if (feedid == null) {\r
-                       return(new Target[0]);\r
-               }\r
-               Feed f = feeds.get(feedid);\r
-               if (f == null) {\r
-                       return(new Target[0]);\r
-               }\r
-               return(f.targets);\r
-       }\r
-       /**\r
-        *      Get the feed ID for a subscription\r
-        *      @param subid    The subscription ID\r
-        *      @return The feed ID\r
-        */\r
-       public String getFeedId(String subid) {\r
-               DestInfo di = subinfo.get(subid);\r
-               if (di == null) {\r
-                       return(null);\r
-               }\r
-               return(di.getLogData());\r
-       }\r
-       /**\r
-        *      Get the spool directory for a subscription\r
-        *      @param subid    The subscription ID\r
-        *      @return The spool directory\r
-        */\r
-       public String getSpoolDir(String subid) {\r
-               DestInfo di = subinfo.get(subid);\r
-               if (di == null) {\r
-                       return(null);\r
-               }\r
-               return(di.getSpool());\r
-       }\r
-       /**\r
-        *      Get the Authorization value this node uses\r
-        *      @return The Authorization header value for this node\r
-        */\r
-       public String getMyAuth() {\r
-               return(myauth);\r
-       }\r
-\r
-}\r
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ *  *      http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ *  * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+
+package org.onap.dmaap.datarouter.node;
+
+import com.att.eelf.configuration.EELFLogger;
+import com.att.eelf.configuration.EELFManager;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+
+/**
+ * Processed configuration for this node.
+ *
+ * <p>The NodeConfig represents a processed configuration from the Data Router provisioning server.  Each time
+ * configuration data is received from the provisioning server, a new NodeConfig is created and the previous one
+ * discarded.
+ */
+public class NodeConfig {
+
+    private static final String PUBLISHER_NOT_PERMITTED = "Publisher not permitted for this feed";
+    private static EELFLogger logger = EELFManager.getInstance().getLogger(NodeConfig.class);
+    private HashMap<String, String> params = new HashMap<>();
+    private HashMap<String, Feed> feeds = new HashMap<>();
+    private HashMap<String, DestInfo> nodeinfo = new HashMap<>();
+    private HashMap<String, DestInfo> subinfo = new HashMap<>();
+    private HashMap<String, IsFrom> nodes = new HashMap<>();
+    private HashMap<String, ProvSubscription> provSubscriptions = new HashMap<>();
+    private String myname;
+    private String myauth;
+    private DestInfo[] alldests;
+    private int rrcntr;
+
+    /**
+     * Process the raw provisioning data to configure this node.
+     *
+     * @param pd The parsed provisioning data
+     * @param myname My name as seen by external systems
+     * @param spooldir The directory where temporary files live
+     * @param port The port number for URLs
+     * @param nodeauthkey The keying string used to generate node authentication credentials
+     */
+    public NodeConfig(ProvData pd, String myname, String spooldir, int port, String nodeauthkey) {
+        this.myname = myname;
+        for (ProvParam p : pd.getParams()) {
+            params.put(p.getName(), p.getValue());
+        }
+        ArrayList<DestInfo> destInfos = new ArrayList<>();
+        myauth = NodeUtils.getNodeAuthHdr(myname, nodeauthkey);
+        for (ProvNode pn : pd.getNodes()) {
+            String commonName = pn.getCName();
+            if (nodeinfo.get(commonName) != null) {
+                continue;
+            }
+            DestInfo di = new DestInfoBuilder().setName("n:" + commonName).setSpool(spooldir + "/n/" + commonName)
+                    .setSubid(null)
+                    .setLogdata("n2n-" + commonName).setUrl("https://" + commonName + ":" + port + "/internal/publish")
+                    .setAuthuser(commonName).setAuthentication(myauth).setMetaonly(false).setUse100(true)
+                    .setPrivilegedSubscriber(false).setFollowRedirects(false).setDecompress(false).createDestInfo();
+            (new File(di.getSpool())).mkdirs();
+            String auth = NodeUtils.getNodeAuthHdr(commonName, nodeauthkey);
+            destInfos.add(di);
+            nodeinfo.put(commonName, di);
+            nodes.put(auth, new IsFrom(commonName));
+        }
+        PathFinder pf = new PathFinder(myname, nodeinfo.keySet().toArray(new String[0]), pd.getHops());
+        HashMap<String, ArrayList<Redirection>> rdtab = new HashMap<>();
+        for (ProvForceIngress pfi : pd.getForceIngress()) {
+            ArrayList<Redirection> v = rdtab.get(pfi.getFeedId());
+            if (v == null) {
+                v = new ArrayList<>();
+                rdtab.put(pfi.getFeedId(), v);
+            }
+            Redirection r = new Redirection();
+            if (pfi.getSubnet() != null) {
+                r.snm = new SubnetMatcher(pfi.getSubnet());
+            }
+            r.user = pfi.getUser();
+            r.nodes = pfi.getNodes();
+            v.add(r);
+        }
+        HashMap<String, HashMap<String, String>> pfutab = new HashMap<>();
+        for (ProvFeedUser pfu : pd.getFeedUsers()) {
+            HashMap<String, String> t = pfutab.get(pfu.getFeedId());
+            if (t == null) {
+                t = new HashMap<>();
+                pfutab.put(pfu.getFeedId(), t);
+            }
+            t.put(pfu.getCredentials(), pfu.getUser());
+        }
+        HashMap<String, String> egrtab = new HashMap<>();
+        for (ProvForceEgress pfe : pd.getForceEgress()) {
+            if (pfe.getNode().equals(myname) || nodeinfo.get(pfe.getNode()) == null) {
+                continue;
+            }
+            egrtab.put(pfe.getSubId(), pfe.getNode());
+        }
+        HashMap<String, ArrayList<SubnetMatcher>> pfstab = new HashMap<>();
+        for (ProvFeedSubnet pfs : pd.getFeedSubnets()) {
+            ArrayList<SubnetMatcher> v = pfstab.get(pfs.getFeedId());
+            if (v == null) {
+                v = new ArrayList<>();
+                pfstab.put(pfs.getFeedId(), v);
+            }
+            v.add(new SubnetMatcher(pfs.getCidr()));
+        }
+        HashMap<String, StringBuilder> feedTargets = new HashMap<>();
+        HashSet<String> allfeeds = new HashSet<>();
+        for (ProvFeed pfx : pd.getFeeds()) {
+            if (pfx.getStatus() == null) {
+                allfeeds.add(pfx.getId());
+            }
+        }
+        for (ProvSubscription provSubscription : pd.getSubscriptions()) {
+            String subId = provSubscription.getSubId();
+            String feedId = provSubscription.getFeedId();
+            if (isFeedOrSubKnown(allfeeds, subId, feedId)) {
+                continue;
+            }
+            int sididx = 999;
+            try {
+                sididx = Integer.parseInt(subId);
+                sididx -= sididx % 100;
+            } catch (Exception e) {
+                logger.error("NODE0517 Exception NodeConfig: " + e);
+            }
+            String subscriptionDirectory = sididx + "/" + subId;
+            DestInfo destinationInfo = new DestInfo("s:" + subId,
+                    spooldir + "/s/" + subscriptionDirectory, provSubscription);
+            (new File(destinationInfo.getSpool())).mkdirs();
+            destInfos.add(destinationInfo);
+            provSubscriptions.put(subId, provSubscription);
+            subinfo.put(subId, destinationInfo);
+            String egr = egrtab.get(subId);
+            if (egr != null) {
+                subId = pf.getPath(egr) + subId;
+            }
+            StringBuilder sb = feedTargets.get(feedId);
+            if (sb == null) {
+                sb = new StringBuilder();
+                feedTargets.put(feedId, sb);
+            }
+            sb.append(' ').append(subId);
+        }
+        alldests = destInfos.toArray(new DestInfo[0]);
+        for (ProvFeed pfx : pd.getFeeds()) {
+            String fid = pfx.getId();
+            Feed f = feeds.get(fid);
+            if (f != null) {
+                continue;
+            }
+            f = new Feed();
+            feeds.put(fid, f);
+            f.createdDate = pfx.getCreatedDate();
+            f.loginfo = pfx.getLogData();
+            f.status = pfx.getStatus();
+            /*
+             * AAF changes: TDP EPIC US# 307413
+             * Passing aafInstance from ProvFeed to identify legacy/AAF feeds
+             */
+            f.aafInstance = pfx.getAafInstance();
+            ArrayList<SubnetMatcher> v1 = pfstab.get(fid);
+            if (v1 == null) {
+                f.subnets = new SubnetMatcher[0];
+            } else {
+                f.subnets = v1.toArray(new SubnetMatcher[0]);
+            }
+            HashMap<String, String> h1 = pfutab.get(fid);
+            if (h1 == null) {
+                h1 = new HashMap();
+            }
+            f.authusers = h1;
+            ArrayList<Redirection> v2 = rdtab.get(fid);
+            if (v2 == null) {
+                f.redirections = new Redirection[0];
+            } else {
+                f.redirections = v2.toArray(new Redirection[0]);
+            }
+            StringBuilder sb = feedTargets.get(fid);
+            if (sb == null) {
+                f.targets = new Target[0];
+            } else {
+                f.targets = parseRouting(sb.toString());
+            }
+        }
+    }
+
+    /**
+     * Parse a target string into an array of targets
+     *
+     * @param routing Target string
+     * @return Array of targets.
+     */
+    public Target[] parseRouting(String routing) {
+        routing = routing.trim();
+        if ("".equals(routing)) {
+            return (new Target[0]);
+        }
+        String[] xx = routing.split("\\s+");
+        HashMap<String, Target> tmap = new HashMap<>();
+        HashSet<String> subset = new HashSet<>();
+        ArrayList<Target> tv = new ArrayList<>();
+        for (int i = 0; i < xx.length; i++) {
+            String t = xx[i];
+            int j = t.indexOf('/');
+            if (j == -1) {
+                addTarget(subset, tv, t);
+            } else {
+                addTargetWithRouting(tmap, tv, t, j);
+            }
+        }
+        return (tv.toArray(new Target[0]));
+    }
+
+    /**
+     * Check whether this is a valid node-to-node transfer
+     *
+     * @param credentials Credentials offered by the supposed node
+     * @param ip IP address the request came from
+     */
+    public boolean isAnotherNode(String credentials, String ip) {
+        IsFrom n = nodes.get(credentials);
+        return (n != null && n.isFrom(ip));
+    }
+
+    /**
+     * Check whether publication is allowed.
+     *
+     * @param feedid The ID of the feed being requested.
+     * @param credentials The offered credentials
+     * @param ip The requesting IP address
+     */
+    public String isPublishPermitted(String feedid, String credentials, String ip) {
+        Feed f = feeds.get(feedid);
+        String nf = "Feed does not exist";
+        if (f != null) {
+            nf = f.status;
+        }
+        if (nf != null) {
+            return (nf);
+        }
+        String user = f.authusers.get(credentials);
+        if (user == null) {
+            return (PUBLISHER_NOT_PERMITTED);
+        }
+        if (f.subnets.length == 0) {
+            return (null);
+        }
+        byte[] addr = NodeUtils.getInetAddress(ip);
+        for (SubnetMatcher snm : f.subnets) {
+            if (snm.matches(addr)) {
+                return (null);
+            }
+        }
+        return (PUBLISHER_NOT_PERMITTED);
+    }
+
+    /**
+     * Check whether delete file is allowed.
+     *
+     * @param subId The ID of the subscription being requested.
+     */
+    public boolean isDeletePermitted(String subId) {
+        ProvSubscription provSubscription = provSubscriptions.get(subId);
+        return provSubscription.isPrivilegedSubscriber();
+    }
+
+    /**
+     * Check whether publication is allowed for AAF Feed.
+     *
+     * @param feedid The ID of the feed being requested.
+     * @param ip The requesting IP address
+     */
+    public String isPublishPermitted(String feedid, String ip) {
+        Feed f = feeds.get(feedid);
+        String nf = "Feed does not exist";
+        if (f != null) {
+            nf = f.status;
+        }
+        if (nf != null) {
+            return nf;
+        }
+        if (f.subnets.length == 0) {
+            return null;
+        }
+        byte[] addr = NodeUtils.getInetAddress(ip);
+        for (SubnetMatcher snm : f.subnets) {
+            if (snm.matches(addr)) {
+                return null;
+            }
+        }
+        return PUBLISHER_NOT_PERMITTED;
+    }
+
+    /**
+     * Get authenticated user
+     */
+    public String getAuthUser(String feedid, String credentials) {
+        return (feeds.get(feedid).authusers.get(credentials));
+    }
+
+    /**
+     * AAF changes: TDP EPIC US# 307413 Check AAF_instance for feed ID
+     *
+     * @param feedid The ID of the feed specified
+     */
+    public String getAafInstance(String feedid) {
+        Feed f = feeds.get(feedid);
+        return f.aafInstance;
+    }
+
+    /**
+     * Check if the request should be redirected to a different ingress node
+     */
+    public String getIngressNode(String feedid, String user, String ip) {
+        Feed f = feeds.get(feedid);
+        if (f.redirections.length == 0) {
+            return (null);
+        }
+        byte[] addr = NodeUtils.getInetAddress(ip);
+        for (Redirection r : f.redirections) {
+            if ((r.user != null && !user.equals(r.user)) || (r.snm != null && !r.snm.matches(addr))) {
+                continue;
+            }
+            for (String n : r.nodes) {
+                if (myname.equals(n)) {
+                    return (null);
+                }
+            }
+            if (r.nodes.length == 0) {
+                return (null);
+            }
+            return (r.nodes[rrcntr++ % r.nodes.length]);
+        }
+        return (null);
+    }
+
+    /**
+     * Get a provisioned configuration parameter
+     */
+    public String getProvParam(String name) {
+        return (params.get(name));
+    }
+
+    /**
+     * Get all the DestInfos
+     */
+    public DestInfo[] getAllDests() {
+        return (alldests);
+    }
+
+    /**
+     * Get the targets for a feed
+     *
+     * @param feedid The feed ID
+     * @return The targets this feed should be delivered to
+     */
+    public Target[] getTargets(String feedid) {
+        if (feedid == null) {
+            return (new Target[0]);
+        }
+        Feed f = feeds.get(feedid);
+        if (f == null) {
+            return (new Target[0]);
+        }
+        return (f.targets);
+    }
+
+    /**
+     * Get the creation date for a feed
+     *
+     * @param feedid The feed ID
+     * @return the timestamp of creation date of feed id passed
+     */
+    public String getCreatedDate(String feedid) {
+        Feed f = feeds.get(feedid);
+        return (f.createdDate);
+    }
+
+    /**
+     * Get the feed ID for a subscription
+     *
+     * @param subid The subscription ID
+     * @return The feed ID
+     */
+    public String getFeedId(String subid) {
+        DestInfo di = subinfo.get(subid);
+        if (di == null) {
+            return (null);
+        }
+        return (di.getLogData());
+    }
+
+    /**
+     * Get the spool directory for a subscription
+     *
+     * @param subid The subscription ID
+     * @return The spool directory
+     */
+    public String getSpoolDir(String subid) {
+        DestInfo di = subinfo.get(subid);
+        if (di == null) {
+            return (null);
+        }
+        return (di.getSpool());
+    }
+
+    /**
+     * Get the Authorization value this node uses
+     *
+     * @return The Authorization header value for this node
+     */
+    public String getMyAuth() {
+        return (myauth);
+    }
+
+    private boolean isFeedOrSubKnown(HashSet<String> allfeeds, String subId, String feedId) {
+        return !allfeeds.contains(feedId) || subinfo.get(subId) != null;
+    }
+
+    private void addTargetWithRouting(HashMap<String, Target> tmap, ArrayList<Target> tv, String t, int j) {
+        String node = t.substring(0, j);
+        String rtg = t.substring(j + 1);
+        DestInfo di = nodeinfo.get(node);
+        if (di == null) {
+            tv.add(new Target(null, t));
+        } else {
+            Target tt = tmap.get(node);
+            if (tt == null) {
+                tt = new Target(di, rtg);
+                tmap.put(node, tt);
+                tv.add(tt);
+            } else {
+                tt.addRouting(rtg);
+            }
+        }
+    }
+
+    private void addTarget(HashSet<String> subset, ArrayList<Target> tv, String t) {
+        DestInfo di = subinfo.get(t);
+        if (di == null) {
+            tv.add(new Target(null, t));
+        } else {
+            if (!subset.contains(t)) {
+                subset.add(t);
+                tv.add(new Target(di, null));
+            }
+        }
+    }
+
+    /**
+     * Raw configuration entry for a data router node
+     */
+    public static class ProvNode {
+
+        private String cname;
+
+        /**
+         * Construct a node configuration entry.
+         *
+         * @param cname The cname of the node.
+         */
+        public ProvNode(String cname) {
+            this.cname = cname;
+        }
+
+        /**
+         * Get the cname of the node
+         */
+        public String getCName() {
+            return (cname);
+        }
+    }
+
+    /**
+     * Raw configuration entry for a provisioning parameter
+     */
+    public static class ProvParam {
+
+        private String name;
+        private String value;
+
+        /**
+         * Construct a provisioning parameter configuration entry.
+         *
+         * @param name The name of the parameter.
+         * @param value The value of the parameter.
+         */
+        public ProvParam(String name, String value) {
+            this.name = name;
+            this.value = value;
+        }
+
+        /**
+         * Get the name of the parameter.
+         */
+        public String getName() {
+            return (name);
+        }
+
+        /**
+         * Get the value of the parameter.
+         */
+        public String getValue() {
+            return (value);
+        }
+    }
+
+    /**
+     * Raw configuration entry for a data feed.
+     */
+    public static class ProvFeed {
+
+        private String id;
+        private String logdata;
+        private String status;
+        private String createdDate;
+        /*
+         * AAF changes: TDP EPIC US# 307413
+         * Passing aafInstance from to identify legacy/AAF feeds
+         */
+        private String aafInstance;
+
+        /**
+         * Construct a feed configuration entry.
+         *
+         * @param id The feed ID of the entry.
+         * @param logdata String for log entries about the entry.
+         * @param status The reason why this feed cannot be used (Feed has been deleted, Feed has been suspended) or
+         * null if it is valid.
+         */
+        public ProvFeed(String id, String logdata, String status, String createdDate, String aafInstance) {
+            this.id = id;
+            this.logdata = logdata;
+            this.status = status;
+            this.createdDate = createdDate;
+            this.aafInstance = aafInstance;
+        }
+
+        /**
+         * Get the created date of the data feed.
+         */
+        public String getCreatedDate() {
+            return (createdDate);
+        }
+
+        /**
+         * Get the aafInstance of the data feed.
+         */
+        public String getAafInstance() {
+            return aafInstance;
+        }
+
+        /**
+         * Get the feed id of the data feed.
+         */
+        public String getId() {
+            return (id);
+        }
+
+        /**
+         * Get the log data of the data feed.
+         */
+        public String getLogData() {
+            return (logdata);
+        }
+
+        /**
+         * Get the status of the data feed.
+         */
+        public String getStatus() {
+            return (status);
+        }
+    }
+
+    /**
+     * Raw configuration entry for a feed user.
+     */
+    public static class ProvFeedUser {
+
+        private String feedid;
+        private String user;
+        private String credentials;
+
+        /**
+         * Construct a feed user configuration entry
+         *
+         * @param feedid The feed id.
+         * @param user The user that will publish to the feed.
+         * @param credentials The Authorization header the user will use to publish.
+         */
+        public ProvFeedUser(String feedid, String user, String credentials) {
+            this.feedid = feedid;
+            this.user = user;
+            this.credentials = credentials;
+        }
+
+        /**
+         * Get the feed id of the feed user.
+         */
+        public String getFeedId() {
+            return (feedid);
+        }
+
+        /**
+         * Get the user for the feed user.
+         */
+        public String getUser() {
+            return (user);
+        }
+
+        /**
+         * Get the credentials for the feed user.
+         */
+        public String getCredentials() {
+            return (credentials);
+        }
+    }
+
+    /**
+     * Raw configuration entry for a feed subnet
+     */
+    public static class ProvFeedSubnet {
+
+        private String feedid;
+        private String cidr;
+
+        /**
+         * Construct a feed subnet configuration entry
+         *
+         * @param feedid The feed ID
+         * @param cidr The CIDR allowed to publish to the feed.
+         */
+        public ProvFeedSubnet(String feedid, String cidr) {
+            this.feedid = feedid;
+            this.cidr = cidr;
+        }
+
+        /**
+         * Get the feed id of the feed subnet.
+         */
+        public String getFeedId() {
+            return (feedid);
+        }
+
+        /**
+         * Get the CIDR of the feed subnet.
+         */
+        public String getCidr() {
+            return (cidr);
+        }
+    }
+
+    /**
+     * Raw configuration entry for a subscription
+     */
+    public static class ProvSubscription {
+
+        private String subid;
+        private String feedid;
+        private String url;
+        private String authuser;
+        private String credentials;
+        private boolean metaonly;
+        private boolean use100;
+        private boolean privilegedSubscriber;
+        private boolean followRedirect;
+        private boolean decompress;
+
+        /**
+         * Construct a subscription configuration entry
+         *
+         * @param subid The subscription ID
+         * @param feedid The feed ID
+         * @param url The base delivery URL (not including the fileid)
+         * @param authuser The user in the credentials used to deliver
+         * @param credentials The credentials used to authenticate to the delivery URL exactly as they go in the
+         * Authorization header.
+         * @param metaonly Is this a meta data only subscription?
+         * @param use100 Should we send Expect: 100-continue?
+         * @param privilegedSubscriber Can we wait to receive a delete file call before deleting file
+         * @param followRedirect Is follow redirect of destination enabled?
+         * @param decompress To see if they want their information compressed or decompressed
+         */
+        public ProvSubscription(String subid, String feedid, String url, String authuser, String credentials,
+                boolean metaonly, boolean use100, boolean privilegedSubscriber, boolean followRedirect,
+                boolean decompress) {
+            this.subid = subid;
+            this.feedid = feedid;
+            this.url = url;
+            this.authuser = authuser;
+            this.credentials = credentials;
+            this.metaonly = metaonly;
+            this.use100 = use100;
+            this.privilegedSubscriber = privilegedSubscriber;
+            this.followRedirect = followRedirect;
+            this.decompress = decompress;
+        }
+
+        /**
+         * Get the subscription ID
+         */
+        public String getSubId() {
+            return (subid);
+        }
+
+        /**
+         * Get the feed ID
+         */
+        public String getFeedId() {
+            return (feedid);
+        }
+
+        /**
+         * Get the delivery URL
+         */
+        public String getURL() {
+            return (url);
+        }
+
+        /**
+         * Get the user
+         */
+        public String getAuthUser() {
+            return (authuser);
+        }
+
+        /**
+         * Get the delivery credentials
+         */
+        public String getCredentials() {
+            return (credentials);
+        }
+
+        /**
+         * Is this a meta data only subscription?
+         */
+        public boolean isMetaDataOnly() {
+            return (metaonly);
+        }
+
+        /**
+         * Should we send Expect: 100-continue?
+         */
+        public boolean isUsing100() {
+            return (use100);
+        }
+
+        /**
+         * Can we wait to receive a delete file call before deleting file
+         */
+        public boolean isPrivilegedSubscriber() {
+            return (privilegedSubscriber);
+        }
+
+        /**
+         * Should i decompress the file before sending it on
+         */
+        public boolean isDecompress() {
+            return (decompress);
+        }
+
+        /**
+         * New field is added - FOLLOW_REDIRECTS feature iTrack:DATARTR-17 - 1706 Get the followRedirect of this
+         * destination
+         */
+        boolean getFollowRedirect() {
+            return (followRedirect);
+        }
+    }
+
+    /**
+     * Raw configuration entry for controlled ingress to the data router node
+     */
+    public static class ProvForceIngress {
+
+        private String feedid;
+        private String subnet;
+        private String user;
+        private String[] nodes;
+
+        /**
+         * Construct a forced ingress configuration entry
+         *
+         * @param feedid The feed ID that this entry applies to
+         * @param subnet The CIDR for which publisher IP addresses this entry applies to or "" if it applies to all
+         * publisher IP addresses
+         * @param user The publishing user this entry applies to or "" if it applies to all publishing users.
+         * @param nodes The array of FQDNs of the data router nodes to redirect publication attempts to.
+         */
+        public ProvForceIngress(String feedid, String subnet, String user, String[] nodes) {
+            this.feedid = feedid;
+            this.subnet = subnet;
+            this.user = user;
+            //Sonar fix
+            if (nodes == null) {
+                this.nodes = new String[0];
+            } else {
+                this.nodes = Arrays.copyOf(nodes, nodes.length);
+            }
+        }
+
+        /**
+         * Get the feed ID
+         */
+        public String getFeedId() {
+            return (feedid);
+        }
+
+        /**
+         * Get the subnet
+         */
+        public String getSubnet() {
+            return (subnet);
+        }
+
+        /**
+         * Get the user
+         */
+        public String getUser() {
+            return (user);
+        }
+
+        /**
+         * Get the node
+         */
+        public String[] getNodes() {
+            return (nodes);
+        }
+    }
+
+    /**
+     * Raw configuration entry for controlled egress from the data router
+     */
+    public static class ProvForceEgress {
+
+        private String subid;
+        private String node;
+
+        /**
+         * Construct a forced egress configuration entry
+         *
+         * @param subid The subscription ID the subscription with forced egress
+         * @param node The node handling deliveries for this subscription
+         */
+        public ProvForceEgress(String subid, String node) {
+            this.subid = subid;
+            this.node = node;
+        }
+
+        /**
+         * Get the subscription ID
+         */
+        public String getSubId() {
+            return (subid);
+        }
+
+        /**
+         * Get the node
+         */
+        public String getNode() {
+            return (node);
+        }
+    }
+
+    /**
+     * Raw configuration entry for routing within the data router network
+     */
+    public static class ProvHop {
+
+        private String from;
+        private String to;
+        private String via;
+
+        /**
+         * Construct a hop entry
+         *
+         * @param from The FQDN of the node with the data to be delivered
+         * @param to The FQDN of the node that will deliver to the subscriber
+         * @param via The FQDN of the node where the from node should send the data
+         */
+        public ProvHop(String from, String to, String via) {
+            this.from = from;
+            this.to = to;
+            this.via = via;
+        }
+
+        /**
+         * A human readable description of this entry
+         */
+        public String toString() {
+            return ("Hop " + from + "->" + to + " via " + via);
+        }
+
+        /**
+         * Get the from node
+         */
+        public String getFrom() {
+            return (from);
+        }
+
+        /**
+         * Get the to node
+         */
+        public String getTo() {
+            return (to);
+        }
+
+        /**
+         * Get the next intermediate node
+         */
+        public String getVia() {
+            return (via);
+        }
+    }
+
+    private static class Redirection {
+
+        SubnetMatcher snm;
+        String user;
+        String[] nodes;
+    }
+
+    private static class Feed {
+
+        String loginfo;
+        String status;
+        SubnetMatcher[] subnets;
+        HashMap<String, String> authusers = new HashMap<>();
+        Redirection[] redirections;
+        Target[] targets;
+        String createdDate;
+        String aafInstance;
+    }
+}