Collection syntax change because of Sonar
[aaf/authz.git] / cadi / client / src / main / java / org / onap / aaf / cadi / http / HClient.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.http;
23
24 import java.io.IOException;
25 import java.io.InputStream;
26 import java.io.InputStreamReader;
27 import java.io.OutputStream;
28 import java.io.Reader;
29 import java.net.HttpURLConnection;
30 import java.net.URI;
31 import java.net.URISyntaxException;
32 import java.net.URL;
33 import java.util.ArrayList;
34
35 import javax.servlet.http.HttpServletResponse;
36
37 import org.onap.aaf.cadi.CadiException;
38 import org.onap.aaf.cadi.LocatorException;
39 import org.onap.aaf.cadi.SecuritySetter;
40 import org.onap.aaf.cadi.client.EClient;
41 import org.onap.aaf.cadi.client.Future;
42 import org.onap.aaf.cadi.client.Rcli;
43 import org.onap.aaf.misc.env.APIException;
44 import org.onap.aaf.misc.env.Data;
45 import org.onap.aaf.misc.env.Data.TYPE;
46 import org.onap.aaf.misc.env.util.Pool.Pooled;
47 import org.onap.aaf.misc.rosetta.env.RosettaDF;
48
49 /**
50  * Low Level Http Client Mechanism. Chances are, you want the high level "HRcli"
51  * for Rosetta Object Translation
52  * 
53  * @author Jonathan
54  *
55  */
56 public class HClient implements EClient<HttpURLConnection> {
57         private URI uri;
58         private ArrayList<Header> headers;
59         private String meth;
60         private String pathinfo;
61         private String query;
62         private String fragment;
63         private Transfer transfer;
64         private SecuritySetter<HttpURLConnection> ss;
65         private HttpURLConnection huc;
66         private int connectTimeout;
67
68         public HClient(SecuritySetter<HttpURLConnection> ss, URI uri,int connectTimeout) throws LocatorException {
69                 if (uri == null) {
70                         throw new LocatorException("No Service available to call");
71                 }
72                 this.uri = uri;
73                 this.ss = ss;
74                 this.connectTimeout = connectTimeout;
75                 pathinfo = query = fragment = null; 
76         }
77
78         @Override
79         public void setMethod(String meth) {
80                 this.meth = meth;
81         }
82
83         @Override
84         public void setPathInfo(String pathinfo) {
85                 this.pathinfo = pathinfo;
86         }
87
88         @Override
89         public void setPayload(Transfer transfer) {
90                 this.transfer = transfer;
91         }
92         
93         @Override
94         public void addHeader(String tag, String value) {
95                 if (headers == null)
96                         headers = new ArrayList<>();
97                 headers.add(new Header(tag, value));
98         }
99
100         @Override
101         public void setQueryParams(String q) {
102                 query = q;
103         }
104
105         @Override
106         public void setFragment(String f) {
107                 fragment = f;
108         }
109
110         @Override
111         public void send() throws APIException {
112                 try {
113                         // Build URL from given URI plus current Settings
114                         if(uri.getPath()==null) {
115                                 throw new APIException("Invalid URL entered for HClient");
116                         }
117                         StringBuilder pi=null;
118                         if(pathinfo!=null) { // additional pathinfo
119                                 pi = new StringBuilder(uri.getPath());
120                                 if(!pathinfo.startsWith("/")) {
121                                         pi.append('/');
122                                 }
123                                 pi.append(pathinfo);
124                         }
125                         URI sendURI = new URI(
126                                         uri.getScheme(),
127                                         uri.getUserInfo(),
128                                         uri.getHost(),
129                                         uri.getPort(),
130                                         pi==null?uri.getPath():pi.toString(),
131                                         query==null?uri.getQuery():query,
132                                         fragment==null?uri.getFragment():fragment
133                                         );
134                         huc = getConnection(sendURI, pi);
135                         huc.setRequestMethod(meth);
136                         if(ss!=null) {
137                                 ss.setSecurity(huc); 
138                         }
139                         if (headers != null)
140                                 for (Header d : headers) {                                      
141                                         huc.addRequestProperty(d.tag, d.value);
142                                 }
143                         huc.setDoInput(true);
144                         huc.setDoOutput(true);
145                         huc.setUseCaches(false);
146                         huc.setConnectTimeout(connectTimeout);
147                         huc.connect();
148                         if (transfer != null) {
149                                 transfer.transfer(huc.getOutputStream());
150                         }
151                         // TODO other settings? There's a bunch here.
152                 } catch (Exception e) {
153                         throw new APIException(e);
154                 } finally { // ensure all these are reset after sends
155                         meth=pathinfo=null;
156                         if(headers!=null) {
157                                 headers.clear();
158                         }
159                         pathinfo = query = fragment = "";
160                 }
161         }
162         
163         public URI getURI() {
164                 return uri;
165         }
166
167         public int timeout() {
168                 return connectTimeout;
169         }
170         
171         protected HttpURLConnection getConnection(URI uri, StringBuilder pi) throws IOException, URISyntaxException {
172                 URL url = new URI(
173                                 uri.getScheme(), 
174                                 uri.getUserInfo(),
175                                 uri.getHost(), 
176                                 uri.getPort(), 
177                                 pi==null?uri.getPath():pi.toString(), 
178                                 query,
179                                 fragment).toURL();
180                 return (HttpURLConnection) url.openConnection();
181         }
182         
183         public abstract class HFuture<T> extends Future<T> {
184                 protected HttpURLConnection huc;
185                 protected int respCode;
186                 protected IOException exception;
187                 protected StringBuilder errContent;
188         
189                 public HFuture(final HttpURLConnection huc) {
190                         this.huc = huc;
191                 }
192         
193                 protected boolean evalInfo(HttpURLConnection huc) throws APIException, IOException{
194                         return respCode == 200;
195                 };
196         
197                 @Override
198                 public final boolean get(int timeout) throws CadiException {
199                         try {
200                                 huc.setReadTimeout(timeout);
201                                 respCode = huc.getResponseCode();
202                                 ss.setLastResponse(respCode);
203                                 if(evalInfo(huc)) {
204                                         return true;
205                                 } else {
206                                         extractError();
207                                         return false;
208                                 }
209                         } catch (IOException | APIException e) {
210                                 throw new CadiException(e);
211                         } finally {
212                                 close();
213                         }
214                 }
215         
216                 private void extractError() {
217                         InputStream is = huc.getErrorStream();
218                         try {
219                                 if(is==null) {
220                                         is = huc.getInputStream();
221                                 }
222                                 if(is!=null) {
223                                 errContent = new StringBuilder();
224                                 int c;
225                                         while((c=is.read())>=0) {
226                                                 errContent.append((char)c);
227                                         }
228                                 }
229                         } catch (IOException e) {
230                                 exception = e;
231                         }
232                 }
233         
234                 // Typically only used by Read
235                 public StringBuilder inputStreamToString(InputStream is) {
236                         // Avoids Carriage returns, and is reasonably efficient, given
237                         // the buffer reads.
238                         try {
239                                 StringBuilder sb = new StringBuilder();
240                                 Reader rdr = new InputStreamReader(is);
241                                 try {
242                                         char[] buf = new char[256];
243                                         int read;
244                                         while ((read = rdr.read(buf)) >= 0) {
245                                                 sb.append(buf, 0, read);
246                                         }
247                                 } finally {
248                                         rdr.close();
249                                 }
250                                 return sb;
251                         } catch (IOException e) {
252                                 exception = e;
253                                 return null;
254                         }
255                 }
256         
257         
258                 @Override
259                 public int code() {
260                         return respCode;
261                 }
262         
263                 public HttpURLConnection huc() {
264                         return huc;
265                 }
266         
267                 public IOException exception() {
268                         return exception;
269                 }
270         
271                 @Override
272                 public String header(String tag) {
273                         return huc.getHeaderField(tag);
274                 }
275         
276                 public void close() {
277                         if(huc!=null) {
278                                 huc.disconnect();
279                         }
280                 }
281         }
282
283         @Override
284         public <T> Future<T> futureCreate(Class<T> t) {
285                 return new HFuture<T>(huc) {
286                         public boolean evalInfo(HttpURLConnection huc) {
287                                 return respCode==201;
288                         }
289
290                         @Override
291                         public String body() {
292                                 if (errContent != null) {
293                                         return errContent.toString();
294                                 }
295                                 return "";
296                         }
297                 };
298         }
299
300         @Override
301         public Future<String> futureReadString() {
302                 return new HFuture<String>(huc) {
303                         public boolean evalInfo(HttpURLConnection huc) throws IOException {
304                                 if (respCode == 200) {
305                                         StringBuilder sb = inputStreamToString(huc.getInputStream());
306                                         if (sb != null) {
307                                                 value = sb.toString();
308                                         }
309                                         return true;
310                                 }
311                                 return false;
312                         }
313
314                         @Override
315                         public String body() {
316                                 if (value != null) {
317                                         return value;
318                                 } else if (errContent != null) {
319                                         return errContent.toString();
320                                 }
321                                 return "";
322                         }
323
324                 };
325         }
326
327         @Override
328         public <T> Future<T> futureRead(final RosettaDF<T> df, final TYPE type) {
329                 return new HFuture<T>(huc) {
330                         private Data<T> data;
331
332                         public boolean evalInfo(HttpURLConnection huc) throws APIException, IOException {
333                                 if (respCode == 200) {
334                                         data = df.newData().in(type).load(huc.getInputStream());
335                                         value = data.asObject();
336                                         return true;
337                                 }
338                                 return false;
339                         }
340
341                         @Override
342                         public String body() {
343                                 if (data != null) {
344                                         try {
345                                                 return data.asString();
346                                         } catch (APIException e) {
347                                         }
348                                 } else if (errContent != null) {
349                                         return errContent.toString();
350                                 }
351                                 return "";
352                         }
353                 };
354         }
355
356         @Override
357         public <T> Future<T> future(final T t) {
358                 return new HFuture<T>(huc) {
359                         public boolean evalInfo(HttpURLConnection huc) {
360                                 if (respCode == 200) {
361                                         value = t;
362                                         return true;
363                                 }
364                                 return false;
365                         }
366
367                         @Override
368                         public String body() {
369                                 if (errContent != null) {
370                                         return errContent.toString();
371                                 }
372                                 return Integer.toString(respCode);
373                         }
374                 };
375         }
376
377         @Override
378         public Future<Void> future(final HttpServletResponse resp, final int expected) throws APIException {
379                 return new HFuture<Void>(huc) {
380                         public boolean evalInfo(HttpURLConnection huc) throws IOException, APIException {
381                                 resp.setStatus(respCode);
382                                 int read;
383                                 InputStream is;
384                                 OutputStream os = resp.getOutputStream();
385                                 if(respCode==expected) {
386                                         is = huc.getInputStream();
387                                         // reuse Buffers
388                                         Pooled<byte[]> pbuff = Rcli.buffPool.get();
389                                         try { 
390                                                 while((read=is.read(pbuff.content))>=0) {
391                                                         os.write(pbuff.content,0,read);
392                                                 }
393                                         } finally {
394                                                 pbuff.done();
395                                         }
396                                         return true;
397                                 } else {
398                                         is = huc.getErrorStream();
399                                         if(is==null) {
400                                                 is = huc.getInputStream();
401                                         }
402                                         if(is!=null) {
403                                                 errContent = new StringBuilder();
404                                                 Pooled<byte[]> pbuff = Rcli.buffPool.get();
405                                                 try { 
406                                                         while((read=is.read(pbuff.content))>=0) {
407                                                                 os.write(pbuff.content,0,read);
408                                                         }
409                                                 } finally {
410                                                         pbuff.done();
411                                                 }
412                                         }
413                                 }
414                                 return false;
415                         }
416
417                         @Override
418                         public String body() {
419                                 return errContent==null?null:errContent.toString();
420                         }
421                 };
422         }
423
424         private static class Header {
425                 public final String tag;
426                 public final String value;
427
428                 public Header(String t, String v) {
429                         this.tag = t;
430                         this.value = v;
431                 }
432                 
433                 public String toString() {
434                         return tag + '=' + value;
435                 }
436         }
437         
438         public String toString() {
439                 return "HttpURLConnection Client configured to " + uri.toString();
440         }
441 }