Remove major and minor code smells in dr-node
[dmaap/datarouter.git] / datarouter-node / src / main / java / org / onap / dmaap / datarouter / node / NodeConfig.java
1 /*******************************************************************************
2  * ============LICENSE_START==================================================
3  * * org.onap.dmaap
4  * * ===========================================================================
5  * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
6  * * ===========================================================================
7  * * Licensed under the Apache License, Version 2.0 (the "License");
8  * * you may not use this file except in compliance with the License.
9  * * You may obtain a copy of the License at
10  * *
11  *  *      http://www.apache.org/licenses/LICENSE-2.0
12  * *
13  *  * Unless required by applicable law or agreed to in writing, software
14  * * distributed under the License is distributed on an "AS IS" BASIS,
15  * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * * See the License for the specific language governing permissions and
17  * * limitations under the License.
18  * * ============LICENSE_END====================================================
19  * *
20  * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
21  * *
22  ******************************************************************************/
23
24
25 package org.onap.dmaap.datarouter.node;
26
27 import com.att.eelf.configuration.EELFLogger;
28 import com.att.eelf.configuration.EELFManager;
29 import java.io.File;
30 import java.util.ArrayList;
31 import java.util.Arrays;
32 import java.util.HashMap;
33 import java.util.HashSet;
34
35 /**
36  * Processed configuration for this node.
37  *
38  * <p>The NodeConfig represents a processed configuration from the Data Router provisioning server.  Each time
39  * configuration data is received from the provisioning server, a new NodeConfig is created and the previous one
40  * discarded.
41  */
42 public class NodeConfig {
43
44     private static final String PUBLISHER_NOT_PERMITTED = "Publisher not permitted for this feed";
45     private static EELFLogger logger = EELFManager.getInstance().getLogger(NodeConfig.class);
46     private HashMap<String, String> params = new HashMap<>();
47     private HashMap<String, Feed> feeds = new HashMap<>();
48     private HashMap<String, DestInfo> nodeinfo = new HashMap<>();
49     private HashMap<String, DestInfo> subinfo = new HashMap<>();
50     private HashMap<String, IsFrom> nodes = new HashMap<>();
51     private HashMap<String, ProvSubscription> provSubscriptions = new HashMap<>();
52     private String myname;
53     private String myauth;
54     private DestInfo[] alldests;
55     private int rrcntr;
56
57     /**
58      * Process the raw provisioning data to configure this node.
59      *
60      * @param pd The parsed provisioning data
61      * @param myname My name as seen by external systems
62      * @param spooldir The directory where temporary files live
63      * @param port The port number for URLs
64      * @param nodeauthkey The keying string used to generate node authentication credentials
65      */
66     public NodeConfig(ProvData pd, String myname, String spooldir, int port, String nodeauthkey) {
67         this.myname = myname;
68         for (ProvParam p : pd.getParams()) {
69             params.put(p.getName(), p.getValue());
70         }
71         ArrayList<DestInfo> destInfos = new ArrayList<>();
72         myauth = NodeUtils.getNodeAuthHdr(myname, nodeauthkey);
73         for (ProvNode pn : pd.getNodes()) {
74             String commonName = pn.getCName();
75             if (nodeinfo.get(commonName) != null) {
76                 continue;
77             }
78             DestInfo di = new DestInfoBuilder().setName("n:" + commonName).setSpool(spooldir + "/n/" + commonName)
79                     .setSubid(null)
80                     .setLogdata("n2n-" + commonName).setUrl("https://" + commonName + ":" + port + "/internal/publish")
81                     .setAuthuser(commonName).setAuthentication(myauth).setMetaonly(false).setUse100(true)
82                     .setPrivilegedSubscriber(false).setFollowRedirects(false).setDecompress(false).createDestInfo();
83             (new File(di.getSpool())).mkdirs();
84             String auth = NodeUtils.getNodeAuthHdr(commonName, nodeauthkey);
85             destInfos.add(di);
86             nodeinfo.put(commonName, di);
87             nodes.put(auth, new IsFrom(commonName));
88         }
89         PathFinder pf = new PathFinder(myname, nodeinfo.keySet().toArray(new String[0]), pd.getHops());
90         HashMap<String, ArrayList<Redirection>> rdtab = new HashMap<>();
91         for (ProvForceIngress pfi : pd.getForceIngress()) {
92             ArrayList<Redirection> v = rdtab.get(pfi.getFeedId());
93             if (v == null) {
94                 v = new ArrayList<>();
95                 rdtab.put(pfi.getFeedId(), v);
96             }
97             Redirection r = new Redirection();
98             if (pfi.getSubnet() != null) {
99                 r.snm = new SubnetMatcher(pfi.getSubnet());
100             }
101             r.user = pfi.getUser();
102             r.nodes = pfi.getNodes();
103             v.add(r);
104         }
105         HashMap<String, HashMap<String, String>> pfutab = new HashMap<>();
106         for (ProvFeedUser pfu : pd.getFeedUsers()) {
107             HashMap<String, String> t = pfutab.get(pfu.getFeedId());
108             if (t == null) {
109                 t = new HashMap<>();
110                 pfutab.put(pfu.getFeedId(), t);
111             }
112             t.put(pfu.getCredentials(), pfu.getUser());
113         }
114         HashMap<String, String> egrtab = new HashMap<>();
115         for (ProvForceEgress pfe : pd.getForceEgress()) {
116             if (pfe.getNode().equals(myname) || nodeinfo.get(pfe.getNode()) == null) {
117                 continue;
118             }
119             egrtab.put(pfe.getSubId(), pfe.getNode());
120         }
121         HashMap<String, ArrayList<SubnetMatcher>> pfstab = new HashMap<>();
122         for (ProvFeedSubnet pfs : pd.getFeedSubnets()) {
123             ArrayList<SubnetMatcher> v = pfstab.get(pfs.getFeedId());
124             if (v == null) {
125                 v = new ArrayList<>();
126                 pfstab.put(pfs.getFeedId(), v);
127             }
128             v.add(new SubnetMatcher(pfs.getCidr()));
129         }
130         HashMap<String, StringBuilder> feedTargets = new HashMap<>();
131         HashSet<String> allfeeds = new HashSet<>();
132         for (ProvFeed pfx : pd.getFeeds()) {
133             if (pfx.getStatus() == null) {
134                 allfeeds.add(pfx.getId());
135             }
136         }
137         for (ProvSubscription provSubscription : pd.getSubscriptions()) {
138             String subId = provSubscription.getSubId();
139             String feedId = provSubscription.getFeedId();
140             if (isFeedOrSubKnown(allfeeds, subId, feedId)) {
141                 continue;
142             }
143             int sididx = 999;
144             try {
145                 sididx = Integer.parseInt(subId);
146                 sididx -= sididx % 100;
147             } catch (Exception e) {
148                 logger.error("NODE0517 Exception NodeConfig: " + e);
149             }
150             String subscriptionDirectory = sididx + "/" + subId;
151             DestInfo destinationInfo = new DestInfo("s:" + subId,
152                     spooldir + "/s/" + subscriptionDirectory, provSubscription);
153             (new File(destinationInfo.getSpool())).mkdirs();
154             destInfos.add(destinationInfo);
155             provSubscriptions.put(subId, provSubscription);
156             subinfo.put(subId, destinationInfo);
157             String egr = egrtab.get(subId);
158             if (egr != null) {
159                 subId = pf.getPath(egr) + subId;
160             }
161             StringBuilder sb = feedTargets.get(feedId);
162             if (sb == null) {
163                 sb = new StringBuilder();
164                 feedTargets.put(feedId, sb);
165             }
166             sb.append(' ').append(subId);
167         }
168         alldests = destInfos.toArray(new DestInfo[0]);
169         for (ProvFeed pfx : pd.getFeeds()) {
170             String fid = pfx.getId();
171             Feed f = feeds.get(fid);
172             if (f != null) {
173                 continue;
174             }
175             f = new Feed();
176             feeds.put(fid, f);
177             f.createdDate = pfx.getCreatedDate();
178             f.loginfo = pfx.getLogData();
179             f.status = pfx.getStatus();
180             /*
181              * AAF changes: TDP EPIC US# 307413
182              * Passing aafInstance from ProvFeed to identify legacy/AAF feeds
183              */
184             f.aafInstance = pfx.getAafInstance();
185             ArrayList<SubnetMatcher> v1 = pfstab.get(fid);
186             if (v1 == null) {
187                 f.subnets = new SubnetMatcher[0];
188             } else {
189                 f.subnets = v1.toArray(new SubnetMatcher[0]);
190             }
191             HashMap<String, String> h1 = pfutab.get(fid);
192             if (h1 == null) {
193                 h1 = new HashMap();
194             }
195             f.authusers = h1;
196             ArrayList<Redirection> v2 = rdtab.get(fid);
197             if (v2 == null) {
198                 f.redirections = new Redirection[0];
199             } else {
200                 f.redirections = v2.toArray(new Redirection[0]);
201             }
202             StringBuilder sb = feedTargets.get(fid);
203             if (sb == null) {
204                 f.targets = new Target[0];
205             } else {
206                 f.targets = parseRouting(sb.toString());
207             }
208         }
209     }
210
211     /**
212      * Parse a target string into an array of targets
213      *
214      * @param routing Target string
215      * @return Array of targets.
216      */
217     public Target[] parseRouting(String routing) {
218         routing = routing.trim();
219         if ("".equals(routing)) {
220             return (new Target[0]);
221         }
222         String[] xx = routing.split("\\s+");
223         HashMap<String, Target> tmap = new HashMap<>();
224         HashSet<String> subset = new HashSet<>();
225         ArrayList<Target> tv = new ArrayList<>();
226         for (int i = 0; i < xx.length; i++) {
227             String t = xx[i];
228             int j = t.indexOf('/');
229             if (j == -1) {
230                 addTarget(subset, tv, t);
231             } else {
232                 addTargetWithRouting(tmap, tv, t, j);
233             }
234         }
235         return (tv.toArray(new Target[0]));
236     }
237
238     /**
239      * Check whether this is a valid node-to-node transfer
240      *
241      * @param credentials Credentials offered by the supposed node
242      * @param ip IP address the request came from
243      */
244     public boolean isAnotherNode(String credentials, String ip) {
245         IsFrom n = nodes.get(credentials);
246         return (n != null && n.isFrom(ip));
247     }
248
249     /**
250      * Check whether publication is allowed.
251      *
252      * @param feedid The ID of the feed being requested.
253      * @param credentials The offered credentials
254      * @param ip The requesting IP address
255      */
256     public String isPublishPermitted(String feedid, String credentials, String ip) {
257         Feed f = feeds.get(feedid);
258         String nf = "Feed does not exist";
259         if (f != null) {
260             nf = f.status;
261         }
262         if (nf != null) {
263             return (nf);
264         }
265         String user = f.authusers.get(credentials);
266         if (user == null) {
267             return (PUBLISHER_NOT_PERMITTED);
268         }
269         if (f.subnets.length == 0) {
270             return (null);
271         }
272         byte[] addr = NodeUtils.getInetAddress(ip);
273         for (SubnetMatcher snm : f.subnets) {
274             if (snm.matches(addr)) {
275                 return (null);
276             }
277         }
278         return (PUBLISHER_NOT_PERMITTED);
279     }
280
281     /**
282      * Check whether delete file is allowed.
283      *
284      * @param subId The ID of the subscription being requested.
285      */
286     public boolean isDeletePermitted(String subId) {
287         ProvSubscription provSubscription = provSubscriptions.get(subId);
288         return provSubscription.isPrivilegedSubscriber();
289     }
290
291     /**
292      * Check whether publication is allowed for AAF Feed.
293      *
294      * @param feedid The ID of the feed being requested.
295      * @param ip The requesting IP address
296      */
297     public String isPublishPermitted(String feedid, String ip) {
298         Feed f = feeds.get(feedid);
299         String nf = "Feed does not exist";
300         if (f != null) {
301             nf = f.status;
302         }
303         if (nf != null) {
304             return nf;
305         }
306         if (f.subnets.length == 0) {
307             return null;
308         }
309         byte[] addr = NodeUtils.getInetAddress(ip);
310         for (SubnetMatcher snm : f.subnets) {
311             if (snm.matches(addr)) {
312                 return null;
313             }
314         }
315         return PUBLISHER_NOT_PERMITTED;
316     }
317
318     /**
319      * Get authenticated user
320      */
321     public String getAuthUser(String feedid, String credentials) {
322         return (feeds.get(feedid).authusers.get(credentials));
323     }
324
325     /**
326      * AAF changes: TDP EPIC US# 307413 Check AAF_instance for feed ID
327      *
328      * @param feedid The ID of the feed specified
329      */
330     public String getAafInstance(String feedid) {
331         Feed f = feeds.get(feedid);
332         return f.aafInstance;
333     }
334
335     /**
336      * Check if the request should be redirected to a different ingress node
337      */
338     public String getIngressNode(String feedid, String user, String ip) {
339         Feed f = feeds.get(feedid);
340         if (f.redirections.length == 0) {
341             return (null);
342         }
343         byte[] addr = NodeUtils.getInetAddress(ip);
344         for (Redirection r : f.redirections) {
345             if ((r.user != null && !user.equals(r.user)) || (r.snm != null && !r.snm.matches(addr))) {
346                 continue;
347             }
348             for (String n : r.nodes) {
349                 if (myname.equals(n)) {
350                     return (null);
351                 }
352             }
353             if (r.nodes.length == 0) {
354                 return (null);
355             }
356             return (r.nodes[rrcntr++ % r.nodes.length]);
357         }
358         return (null);
359     }
360
361     /**
362      * Get a provisioned configuration parameter
363      */
364     public String getProvParam(String name) {
365         return (params.get(name));
366     }
367
368     /**
369      * Get all the DestInfos
370      */
371     public DestInfo[] getAllDests() {
372         return (alldests);
373     }
374
375     /**
376      * Get the targets for a feed
377      *
378      * @param feedid The feed ID
379      * @return The targets this feed should be delivered to
380      */
381     public Target[] getTargets(String feedid) {
382         if (feedid == null) {
383             return (new Target[0]);
384         }
385         Feed f = feeds.get(feedid);
386         if (f == null) {
387             return (new Target[0]);
388         }
389         return (f.targets);
390     }
391
392     /**
393      * Get the creation date for a feed
394      *
395      * @param feedid The feed ID
396      * @return the timestamp of creation date of feed id passed
397      */
398     public String getCreatedDate(String feedid) {
399         Feed f = feeds.get(feedid);
400         return (f.createdDate);
401     }
402
403     /**
404      * Get the feed ID for a subscription
405      *
406      * @param subid The subscription ID
407      * @return The feed ID
408      */
409     public String getFeedId(String subid) {
410         DestInfo di = subinfo.get(subid);
411         if (di == null) {
412             return (null);
413         }
414         return (di.getLogData());
415     }
416
417     /**
418      * Get the spool directory for a subscription
419      *
420      * @param subid The subscription ID
421      * @return The spool directory
422      */
423     public String getSpoolDir(String subid) {
424         DestInfo di = subinfo.get(subid);
425         if (di == null) {
426             return (null);
427         }
428         return (di.getSpool());
429     }
430
431     /**
432      * Get the Authorization value this node uses
433      *
434      * @return The Authorization header value for this node
435      */
436     public String getMyAuth() {
437         return (myauth);
438     }
439
440     private boolean isFeedOrSubKnown(HashSet<String> allfeeds, String subId, String feedId) {
441         return !allfeeds.contains(feedId) || subinfo.get(subId) != null;
442     }
443
444     private void addTargetWithRouting(HashMap<String, Target> tmap, ArrayList<Target> tv, String t, int j) {
445         String node = t.substring(0, j);
446         String rtg = t.substring(j + 1);
447         DestInfo di = nodeinfo.get(node);
448         if (di == null) {
449             tv.add(new Target(null, t));
450         } else {
451             Target tt = tmap.get(node);
452             if (tt == null) {
453                 tt = new Target(di, rtg);
454                 tmap.put(node, tt);
455                 tv.add(tt);
456             } else {
457                 tt.addRouting(rtg);
458             }
459         }
460     }
461
462     private void addTarget(HashSet<String> subset, ArrayList<Target> tv, String t) {
463         DestInfo di = subinfo.get(t);
464         if (di == null) {
465             tv.add(new Target(null, t));
466         } else {
467             if (!subset.contains(t)) {
468                 subset.add(t);
469                 tv.add(new Target(di, null));
470             }
471         }
472     }
473
474     /**
475      * Raw configuration entry for a data router node
476      */
477     public static class ProvNode {
478
479         private String cname;
480
481         /**
482          * Construct a node configuration entry.
483          *
484          * @param cname The cname of the node.
485          */
486         public ProvNode(String cname) {
487             this.cname = cname;
488         }
489
490         /**
491          * Get the cname of the node
492          */
493         public String getCName() {
494             return (cname);
495         }
496     }
497
498     /**
499      * Raw configuration entry for a provisioning parameter
500      */
501     public static class ProvParam {
502
503         private String name;
504         private String value;
505
506         /**
507          * Construct a provisioning parameter configuration entry.
508          *
509          * @param name The name of the parameter.
510          * @param value The value of the parameter.
511          */
512         public ProvParam(String name, String value) {
513             this.name = name;
514             this.value = value;
515         }
516
517         /**
518          * Get the name of the parameter.
519          */
520         public String getName() {
521             return (name);
522         }
523
524         /**
525          * Get the value of the parameter.
526          */
527         public String getValue() {
528             return (value);
529         }
530     }
531
532     /**
533      * Raw configuration entry for a data feed.
534      */
535     public static class ProvFeed {
536
537         private String id;
538         private String logdata;
539         private String status;
540         private String createdDate;
541         /*
542          * AAF changes: TDP EPIC US# 307413
543          * Passing aafInstance from to identify legacy/AAF feeds
544          */
545         private String aafInstance;
546
547         /**
548          * Construct a feed configuration entry.
549          *
550          * @param id The feed ID of the entry.
551          * @param logdata String for log entries about the entry.
552          * @param status The reason why this feed cannot be used (Feed has been deleted, Feed has been suspended) or
553          * null if it is valid.
554          */
555         public ProvFeed(String id, String logdata, String status, String createdDate, String aafInstance) {
556             this.id = id;
557             this.logdata = logdata;
558             this.status = status;
559             this.createdDate = createdDate;
560             this.aafInstance = aafInstance;
561         }
562
563         /**
564          * Get the created date of the data feed.
565          */
566         public String getCreatedDate() {
567             return (createdDate);
568         }
569
570         /**
571          * Get the aafInstance of the data feed.
572          */
573         public String getAafInstance() {
574             return aafInstance;
575         }
576
577         /**
578          * Get the feed id of the data feed.
579          */
580         public String getId() {
581             return (id);
582         }
583
584         /**
585          * Get the log data of the data feed.
586          */
587         public String getLogData() {
588             return (logdata);
589         }
590
591         /**
592          * Get the status of the data feed.
593          */
594         public String getStatus() {
595             return (status);
596         }
597     }
598
599     /**
600      * Raw configuration entry for a feed user.
601      */
602     public static class ProvFeedUser {
603
604         private String feedid;
605         private String user;
606         private String credentials;
607
608         /**
609          * Construct a feed user configuration entry
610          *
611          * @param feedid The feed id.
612          * @param user The user that will publish to the feed.
613          * @param credentials The Authorization header the user will use to publish.
614          */
615         public ProvFeedUser(String feedid, String user, String credentials) {
616             this.feedid = feedid;
617             this.user = user;
618             this.credentials = credentials;
619         }
620
621         /**
622          * Get the feed id of the feed user.
623          */
624         public String getFeedId() {
625             return (feedid);
626         }
627
628         /**
629          * Get the user for the feed user.
630          */
631         public String getUser() {
632             return (user);
633         }
634
635         /**
636          * Get the credentials for the feed user.
637          */
638         public String getCredentials() {
639             return (credentials);
640         }
641     }
642
643     /**
644      * Raw configuration entry for a feed subnet
645      */
646     public static class ProvFeedSubnet {
647
648         private String feedid;
649         private String cidr;
650
651         /**
652          * Construct a feed subnet configuration entry
653          *
654          * @param feedid The feed ID
655          * @param cidr The CIDR allowed to publish to the feed.
656          */
657         public ProvFeedSubnet(String feedid, String cidr) {
658             this.feedid = feedid;
659             this.cidr = cidr;
660         }
661
662         /**
663          * Get the feed id of the feed subnet.
664          */
665         public String getFeedId() {
666             return (feedid);
667         }
668
669         /**
670          * Get the CIDR of the feed subnet.
671          */
672         public String getCidr() {
673             return (cidr);
674         }
675     }
676
677     /**
678      * Raw configuration entry for a subscription
679      */
680     public static class ProvSubscription {
681
682         private String subid;
683         private String feedid;
684         private String url;
685         private String authuser;
686         private String credentials;
687         private boolean metaonly;
688         private boolean use100;
689         private boolean privilegedSubscriber;
690         private boolean followRedirect;
691         private boolean decompress;
692
693         /**
694          * Construct a subscription configuration entry
695          *
696          * @param subid The subscription ID
697          * @param feedid The feed ID
698          * @param url The base delivery URL (not including the fileid)
699          * @param authuser The user in the credentials used to deliver
700          * @param credentials The credentials used to authenticate to the delivery URL exactly as they go in the
701          * Authorization header.
702          * @param metaonly Is this a meta data only subscription?
703          * @param use100 Should we send Expect: 100-continue?
704          * @param privilegedSubscriber Can we wait to receive a delete file call before deleting file
705          * @param followRedirect Is follow redirect of destination enabled?
706          * @param decompress To see if they want their information compressed or decompressed
707          */
708         public ProvSubscription(String subid, String feedid, String url, String authuser, String credentials,
709                 boolean metaonly, boolean use100, boolean privilegedSubscriber, boolean followRedirect,
710                 boolean decompress) {
711             this.subid = subid;
712             this.feedid = feedid;
713             this.url = url;
714             this.authuser = authuser;
715             this.credentials = credentials;
716             this.metaonly = metaonly;
717             this.use100 = use100;
718             this.privilegedSubscriber = privilegedSubscriber;
719             this.followRedirect = followRedirect;
720             this.decompress = decompress;
721         }
722
723         /**
724          * Get the subscription ID
725          */
726         public String getSubId() {
727             return (subid);
728         }
729
730         /**
731          * Get the feed ID
732          */
733         public String getFeedId() {
734             return (feedid);
735         }
736
737         /**
738          * Get the delivery URL
739          */
740         public String getURL() {
741             return (url);
742         }
743
744         /**
745          * Get the user
746          */
747         public String getAuthUser() {
748             return (authuser);
749         }
750
751         /**
752          * Get the delivery credentials
753          */
754         public String getCredentials() {
755             return (credentials);
756         }
757
758         /**
759          * Is this a meta data only subscription?
760          */
761         public boolean isMetaDataOnly() {
762             return (metaonly);
763         }
764
765         /**
766          * Should we send Expect: 100-continue?
767          */
768         public boolean isUsing100() {
769             return (use100);
770         }
771
772         /**
773          * Can we wait to receive a delete file call before deleting file
774          */
775         public boolean isPrivilegedSubscriber() {
776             return (privilegedSubscriber);
777         }
778
779         /**
780          * Should i decompress the file before sending it on
781          */
782         public boolean isDecompress() {
783             return (decompress);
784         }
785
786         /**
787          * New field is added - FOLLOW_REDIRECTS feature iTrack:DATARTR-17 - 1706 Get the followRedirect of this
788          * destination
789          */
790         boolean getFollowRedirect() {
791             return (followRedirect);
792         }
793     }
794
795     /**
796      * Raw configuration entry for controlled ingress to the data router node
797      */
798     public static class ProvForceIngress {
799
800         private String feedid;
801         private String subnet;
802         private String user;
803         private String[] nodes;
804
805         /**
806          * Construct a forced ingress configuration entry
807          *
808          * @param feedid The feed ID that this entry applies to
809          * @param subnet The CIDR for which publisher IP addresses this entry applies to or "" if it applies to all
810          * publisher IP addresses
811          * @param user The publishing user this entry applies to or "" if it applies to all publishing users.
812          * @param nodes The array of FQDNs of the data router nodes to redirect publication attempts to.
813          */
814         public ProvForceIngress(String feedid, String subnet, String user, String[] nodes) {
815             this.feedid = feedid;
816             this.subnet = subnet;
817             this.user = user;
818             //Sonar fix
819             if (nodes == null) {
820                 this.nodes = new String[0];
821             } else {
822                 this.nodes = Arrays.copyOf(nodes, nodes.length);
823             }
824         }
825
826         /**
827          * Get the feed ID
828          */
829         public String getFeedId() {
830             return (feedid);
831         }
832
833         /**
834          * Get the subnet
835          */
836         public String getSubnet() {
837             return (subnet);
838         }
839
840         /**
841          * Get the user
842          */
843         public String getUser() {
844             return (user);
845         }
846
847         /**
848          * Get the node
849          */
850         public String[] getNodes() {
851             return (nodes);
852         }
853     }
854
855     /**
856      * Raw configuration entry for controlled egress from the data router
857      */
858     public static class ProvForceEgress {
859
860         private String subid;
861         private String node;
862
863         /**
864          * Construct a forced egress configuration entry
865          *
866          * @param subid The subscription ID the subscription with forced egress
867          * @param node The node handling deliveries for this subscription
868          */
869         public ProvForceEgress(String subid, String node) {
870             this.subid = subid;
871             this.node = node;
872         }
873
874         /**
875          * Get the subscription ID
876          */
877         public String getSubId() {
878             return (subid);
879         }
880
881         /**
882          * Get the node
883          */
884         public String getNode() {
885             return (node);
886         }
887     }
888
889     /**
890      * Raw configuration entry for routing within the data router network
891      */
892     public static class ProvHop {
893
894         private String from;
895         private String to;
896         private String via;
897
898         /**
899          * Construct a hop entry
900          *
901          * @param from The FQDN of the node with the data to be delivered
902          * @param to The FQDN of the node that will deliver to the subscriber
903          * @param via The FQDN of the node where the from node should send the data
904          */
905         public ProvHop(String from, String to, String via) {
906             this.from = from;
907             this.to = to;
908             this.via = via;
909         }
910
911         /**
912          * A human readable description of this entry
913          */
914         public String toString() {
915             return ("Hop " + from + "->" + to + " via " + via);
916         }
917
918         /**
919          * Get the from node
920          */
921         public String getFrom() {
922             return (from);
923         }
924
925         /**
926          * Get the to node
927          */
928         public String getTo() {
929             return (to);
930         }
931
932         /**
933          * Get the next intermediate node
934          */
935         public String getVia() {
936             return (via);
937         }
938     }
939
940     private static class Redirection {
941
942         SubnetMatcher snm;
943         String user;
944         String[] nodes;
945     }
946
947     private static class Feed {
948
949         String loginfo;
950         String status;
951         SubnetMatcher[] subnets;
952         HashMap<String, String> authusers = new HashMap<>();
953         Redirection[] redirections;
954         Target[] targets;
955         String createdDate;
956         String aafInstance;
957     }
958 }