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