Fix DNSLocator JUnit
[aaf/authz.git] / cadi / client / src / main / java / org / onap / aaf / cadi / locator / DNSLocator.java
1 /**
2  * ============LICENSE_START====================================================
3  * org.onap.aaf
4  * ===========================================================================
5  * Copyright (c) 2018 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  */
21
22 package org.onap.aaf.cadi.locator;
23
24 import java.io.IOException;
25 import java.net.InetAddress;
26 //import java.net.InetAddress;
27 import java.net.URI;
28 import java.net.URISyntaxException;
29 import java.net.UnknownHostException;
30
31 import org.onap.aaf.cadi.Access;
32 import org.onap.aaf.cadi.Access.Level;
33 import org.onap.aaf.cadi.LocatorException;
34
35 public class DNSLocator implements SizedLocator<URI> {
36     private static enum Status {UNTRIED, OK, INVALID, SLOW};
37     private static final int CHECK_TIME = 3000;
38
39     private String host, protocol;
40     private Access access;
41     private Host[] hosts;
42     private int startPort, endPort;
43     private String suffix;
44     // Added for JUnit Purposes.  Force correct responses in odd situations on tests
45     private final DNSLookup dnsLookup;
46
47     private int size = 1; // initial, until refreshed.
48
49     public DNSLocator(Access access, String protocol, String host, String range) {
50         this(access, protocol, host, range, DNSLookup.dflt);
51     }
52     
53     public DNSLocator(Access access, String protocol, String host, String range, DNSLookup dnsLookup) {
54         this.host = host;
55         this.protocol = protocol;
56         this.access = access;
57         this.dnsLookup = dnsLookup;
58          
59         int dash = range.indexOf('-');
60         if (dash<0) {
61             startPort = endPort = Integer.parseInt(range);
62         } else {
63             startPort = Integer.parseInt(range.substring(0,dash));
64             endPort = Integer.parseInt(range.substring(dash + 1));
65         }
66         refresh();
67     }
68
69     public DNSLocator(Access access, String aaf_locate) throws LocatorException {
70         this(access, aaf_locate, DNSLookup.dflt);
71     }
72     
73     public DNSLocator(Access access, String aaf_locate, DNSLookup dnsLookup) throws LocatorException {
74         this.access = access;
75         this.dnsLookup = dnsLookup;
76         if (aaf_locate==null) {
77             throw new LocatorException("Null passed into DNSLocator constructor");
78         }
79         int start, defPort;
80         if (aaf_locate.startsWith("https://")) {
81             protocol = "https";
82             start = 8; // https://
83             defPort = 443;
84         } else if (aaf_locate.startsWith("http://")) {
85             protocol = "http";
86             start = 7; // http://
87             defPort = 80;
88         } else {
89             throw new LocatorException("DNSLocator accepts only https or http protocols.  (requested URL " + aaf_locate + ')');
90         }
91         host = parseHostAndPorts(aaf_locate, start, defPort);
92         refresh();
93     }
94
95     public static DNSLocator create(Access access, String url) throws LocatorException {
96         return new DNSLocator(access, url);
97     }
98
99     @Override
100     public URI get(Item item) throws LocatorException {
101         return hosts[((DLItem)item).cnt].uri;
102     }
103
104     @Override
105     public boolean hasItems() {
106         for (Host h : hosts) {
107             if (h.status==Status.OK) {
108                 return true;
109             }
110         }
111         return false;
112     }
113
114     @Override
115     public void invalidate(Item item) {
116         DLItem di = (DLItem)item;
117         hosts[di.cnt].status = Status.INVALID;
118     }
119
120     @Override
121     public Item best() throws LocatorException {
122         // not a good "best"
123         for (int i=0;i<hosts.length;++i) {
124             switch(hosts[i].status) {
125                 case OK:
126                     return new DLItem(i);
127                 case INVALID:
128                     break;
129                 case SLOW:
130                     break;
131                 case UNTRIED:
132                     try {
133                         if (hosts[i].ia.isReachable(CHECK_TIME)) {
134                             hosts[i].status = Status.OK;
135                             return new DLItem(i);
136                         }
137                     } catch (IOException e) {
138                         throw new LocatorException(e);
139                     }
140                     break;
141                 default:
142                     break;
143             }
144         }
145         throw new LocatorException("No Available URIs for " + host);
146     }
147
148     @Override
149     public Item first() throws LocatorException {
150         return new DLItem(0);
151     }
152
153     @Override
154     public Item next(Item item) throws LocatorException {
155         DLItem di = (DLItem)item;
156         if (++di.cnt<hosts.length) {
157             return di;
158         } else {
159             return null;
160         }
161     }
162
163     @Override
164     public boolean refresh() {
165         try {
166             InetAddress[] ias = dnsLookup.getAllByName(host);
167             Host[] temp = new Host[ias.length * (1 + endPort - startPort)];
168             int cnt = -1;
169             for (int j=startPort; j<=endPort; ++j) {
170                 for (int i=0;i<ias.length;++i) {
171                     temp[++cnt] = new Host(ias[i], j, suffix);
172                 }
173             }
174             hosts = temp;
175             size = temp.length * (endPort-startPort+1);
176             return true;
177         } catch (Exception e) {
178             access.log(Level.ERROR, e);
179         }
180         return false;
181     }
182
183     private String parseHostAndPorts(String aaf_locate, int _start, int defaultPort) throws LocatorException {
184         int slash, start;
185         int colon = aaf_locate.indexOf(':',_start);
186         if (colon > 0) {
187             host = aaf_locate.substring(_start,colon);
188             start = colon + 1;
189             int left = aaf_locate.indexOf('[', start);
190             if (left > 0) {
191                 int right = aaf_locate.indexOf(']', left + 1);
192                 if (right < 0) {
193                     throw new LocatorException("Missing closing bracket in DNSLocator constructor.  (requested URL " + aaf_locate + ')');
194                 } else if (right == (left + 1)) {
195                     throw new LocatorException("Missing ports in brackets in DNSLocator constructor.  (requested URL " + aaf_locate + ')');
196                 }
197                 int dash = aaf_locate.indexOf('-', left + 1);
198                 if (dash == (right - 1) || dash == (left + 1)) {
199                     throw new LocatorException("Missing ports in brackets in DNSLocator constructor.  (requested URL " + aaf_locate + ')');
200                 }
201                 if (dash < 0) {
202                     startPort = endPort = Integer.parseInt(aaf_locate.substring(left + 1, right));
203                 } else {
204                     startPort = Integer.parseInt(aaf_locate.substring(left + 1, dash));
205                     endPort = Integer.parseInt(aaf_locate.substring(dash + 1, right));
206                 }
207                 slash = aaf_locate.indexOf('/', start);
208                 if(slash>=0) {
209                     suffix = aaf_locate.substring(slash);
210                 }
211
212             } else {
213                 slash = aaf_locate.indexOf('/', start);
214                 if (slash == start) {
215                     throw new LocatorException("Missing port before '/' in DNSLocator constructor.  (requested URL " + aaf_locate + ')');
216                 }
217                 if (slash < 0) {
218                     startPort = endPort = Integer.parseInt(aaf_locate.substring(start));
219                 } else {
220                     startPort = endPort = Integer.parseInt(aaf_locate.substring(start, slash));
221                     suffix = aaf_locate.substring(slash);
222                 }
223             }
224         } else {
225             slash = aaf_locate.indexOf('/', _start);
226             host = slash<_start?aaf_locate.substring(_start):aaf_locate.substring(_start,slash);
227             startPort = endPort = defaultPort;
228         }
229
230         return host;
231     }
232     
233     /**
234      * 
235      * Some ISPs return InetEntries for bogus entries, making JUnit Testing difficult.
236      * We add this interface to force validly empty responses.
237      * We don't use Lambda at this point, so we can continue to support JKD 1.7 
238      * a while longer.
239      *
240      */
241     public interface DNSLookup {
242         InetAddress[] getAllByName(String host) throws UnknownHostException;
243         public static final DNSLookup dflt = new DNSLookup() {
244                 public InetAddress[] getAllByName(String host) throws UnknownHostException {
245                         return InetAddress.getAllByName(host);
246                 }
247         };
248     }
249     
250     private class Host {
251         private URI uri;
252         private InetAddress ia;
253         private Status status;
254
255         public Host(InetAddress inetAddress, int port, String suffix) throws URISyntaxException {
256             ia = inetAddress;
257             uri = new URI(protocol,null,inetAddress.getCanonicalHostName(),port,suffix,null,null);
258             status = Status.UNTRIED;
259         }
260
261         public String toString() {
262             return uri.toString() + " - " + status.name();
263         }
264     }
265
266     private class DLItem implements Item {
267         public DLItem(int i) {
268             cnt = i;
269         }
270
271         private int cnt;
272     }
273
274     public void destroy() {}
275
276     public int size() {
277         return size;
278     }
279 }