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