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