2 * ============LICENSE_START====================================================
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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====================================================
22 package org.onap.aaf.cadi.client;
24 import java.io.IOException;
25 import java.io.OutputStream;
26 import java.io.PrintStream;
28 import java.util.Enumeration;
30 import javax.servlet.ServletInputStream;
31 import javax.servlet.http.HttpServletRequest;
32 import javax.servlet.http.HttpServletResponse;
34 import org.onap.aaf.cadi.CadiException;
35 import org.onap.aaf.cadi.SecuritySetter;
36 import org.onap.aaf.cadi.client.EClient.Transfer;
37 import org.onap.aaf.misc.env.APIException;
38 import org.onap.aaf.misc.env.Data.TYPE;
39 import org.onap.aaf.misc.env.util.Pool;
40 import org.onap.aaf.misc.env.util.Pool.Pooled;
41 import org.onap.aaf.misc.rosetta.env.RosettaDF;
43 public abstract class Rcli<CT> {
44 public static final String FORM_ENCODED = "application/x-www-form-urlencoded";
45 public static final String APPL_JSON = "application/json";
46 public static final String APPL_XML = "application/xml";
47 public static final String BLANK = "";
48 public static final String CONTENT_TYPE = "Content-Type";
49 public static final String ACCEPT = "Accept";
51 protected static final String POST = "POST";
52 protected static final String GET = "GET";
53 protected static final String PUT = "PUT";
54 protected static final String DELETE = "DELETE";
56 protected String apiVersion;
57 protected int readTimeout = 5000;
58 protected int connectionTimeout = 3000;
60 private String oneCallQueryParams;
61 public static Pool<byte[]> buffPool = new Pool<byte[]>(new Pool.Creator<byte[]>() {
63 public byte[] create() throws APIException {
64 return new byte[1024];
68 public void destroy(byte[] t) {
72 public boolean isValid(byte[] t) {
77 public void reuse(byte[] t) {
86 public abstract void setSecuritySetter(SecuritySetter<CT> ss);
87 public abstract SecuritySetter<CT> getSecuritySetter();
90 public Rcli<CT> forUser(SecuritySetter<CT> ss) {
91 Rcli<CT> rv = clone(uri==null?this.uri:uri,ss);
92 setSecuritySetter(ss);
94 rv.apiVersion = apiVersion;
98 protected abstract Rcli<CT> clone(URI uri, SecuritySetter<CT> ss);
100 public abstract void invalidate() throws CadiException;
102 public Rcli<CT> readTimeout(int millis) {
103 readTimeout = millis;
107 public Rcli<CT> connectionTimeout(int millis) {
108 connectionTimeout = millis;
112 public Rcli<CT> type(TYPE type) {
117 public Rcli<CT> apiVersion(String apiVersion) {
118 this.apiVersion = apiVersion;
122 public boolean isApiVersion(String prospective) {
123 return apiVersion.equals(prospective);
127 public String typeString(Class<?> cls) {
128 return "application/"+cls.getSimpleName()+"+"+type.name().toLowerCase()+
129 (apiVersion==null?BLANK:";version="+apiVersion);
132 protected abstract EClient<CT> client() throws CadiException;
135 public<T> Future<T> create(final String pathinfo, final String contentType, final RosettaDF<T> df, final T t) throws APIException, CadiException {
136 final ParsePath pp = new ParsePath(pathinfo);
138 EClient<CT> client = client();
139 client.setMethod(POST);
140 client.addHeader(CONTENT_TYPE,contentType);
141 client.setPathInfo(pp.path());
142 client.setQueryParams(pp.query());
143 client.setFragment(pp.frag());
144 client.setPayload(new EClient.Transfer() {
146 public void transfer(OutputStream os) throws IOException, APIException {
147 df.newData().out(type).direct(t,os);
151 return client.futureCreate(df.getTypeClass());
154 public<T> Future<T> create(String pathinfo, final RosettaDF<T> df, final T t) throws APIException, CadiException {
155 final ParsePath pp = new ParsePath(pathinfo);
157 EClient<CT> client = client();
158 client.setMethod(POST);
159 client.addHeader(CONTENT_TYPE,typeString(df.getTypeClass()));
160 client.setPathInfo(pp.path());
161 client.setQueryParams(pp.query());
162 client.setFragment(pp.frag());
163 client.setPayload(new EClient.Transfer() {
165 public void transfer(OutputStream os) throws IOException, APIException {
166 df.newData().out(type).direct(t,os);
170 return client.futureCreate(df.getTypeClass());
173 public<T> Future<T> create(String pathinfo, Class<?> cls, final RosettaDF<T> df, final T t) throws APIException, CadiException {
174 final ParsePath pp = new ParsePath(pathinfo);
176 EClient<CT> client = client();
177 client.setMethod(POST);
178 client.addHeader(CONTENT_TYPE,typeString(cls));
179 client.setPathInfo(pp.path());
180 client.setQueryParams(pp.query());
181 client.setFragment(pp.frag());
182 client.setPayload(new EClient.Transfer() {
184 public void transfer(OutputStream os) throws IOException, APIException {
185 df.newData().out(type).direct(t,os);
189 return client.futureCreate(df.getTypeClass());
192 public<T> Future<T> create(String pathinfo, Class<T> cls) throws APIException, CadiException {
193 final ParsePath pp = new ParsePath(pathinfo);
195 EClient<CT> client = client();
196 client.setMethod(POST);
197 client.addHeader(CONTENT_TYPE,typeString(cls));
198 client.setPathInfo(pp.path());
199 client.setQueryParams(pp.query());
200 client.setFragment(pp.frag());
201 client.setPayload(null);
203 return client.futureCreate(cls);
206 public Future<Void> create(String pathinfo, String contentType) throws APIException, CadiException {
207 final ParsePath pp = new ParsePath(pathinfo);
209 EClient<CT> client = client();
210 client.setMethod(POST);
211 client.addHeader(CONTENT_TYPE,contentType);
212 client.setPathInfo(pp.path());
213 client.setQueryParams(pp.query());
214 client.setFragment(pp.frag());
215 client.setPayload(null);
217 return client.futureCreate(Void.class);
222 * Post Data in WWW expected format, with the format tag1=value1&tag2=value2, etc
224 * Because typically, you will want to have a variable as value, you can type, as long as tag ends with "="
225 * postForm(..., "tag1=value1","tag2=",var2);
231 * @throws APIException
232 * @throws CadiException
234 public <T> Future<T> postForm(String pathinfo, final RosettaDF<T> df, final String ... formParam) throws APIException, CadiException {
235 final ParsePath pp = new ParsePath(pathinfo);
237 EClient<CT> client = client();
238 client.setMethod(POST);
239 client.addHeader(CONTENT_TYPE,FORM_ENCODED);
242 client.addHeader(ACCEPT, APPL_JSON);
245 client.addHeader(ACCEPT, APPL_XML);
250 client.setPathInfo(pp.path());
251 client.setQueryParams(pp.query());
252 client.setFragment(pp.frag());
253 client.setPayload(new Transfer() {
255 public void transfer(OutputStream os) throws IOException, APIException {
257 if(os instanceof PrintStream) {
258 ps = (PrintStream)os;
260 ps = new PrintStream(os);
262 boolean first = true;
263 for(String fp : formParam) {
270 if(fp.endsWith("=")) {
278 return client.futureRead(df,TYPE.JSON);
282 * Read String, using POST for keyInfo
289 * @throws APIException
290 * @throws CadiException
292 public<T> Future<String> readPost(String pathinfo, final RosettaDF<T> df, final T t) throws APIException, CadiException {
293 final ParsePath pp = new ParsePath(pathinfo);
295 EClient<CT> client = client();
296 client.setMethod(POST);
297 client.addHeader(CONTENT_TYPE,typeString(df.getTypeClass()));
298 client.setPathInfo(pp.path());
299 client.setQueryParams(pp.query());
300 client.setFragment(pp.frag());
301 client.setPayload(new EClient.Transfer() {
303 public void transfer(OutputStream os) throws IOException, APIException {
304 df.newData().out(type).direct(t,os);
308 return client.futureReadString();
312 * Read using POST for keyInfo, responding with marshaled Objects
319 * @throws APIException
320 * @throws CadiException
322 public<T,R> Future<R> readPost(String pathinfo, final RosettaDF<T> df, final T t, final RosettaDF<R> resp) throws APIException, CadiException {
323 final ParsePath pp = new ParsePath(pathinfo);
325 EClient<CT> client = client();
326 client.setMethod(POST);
327 client.addHeader(CONTENT_TYPE,typeString(df.getTypeClass()));
328 client.setPathInfo(pp.path());
329 client.setQueryParams(pp.query());
330 client.setFragment(pp.frag());
331 client.setPayload(new EClient.Transfer() {
333 public void transfer(OutputStream os) throws IOException, APIException {
334 df.newData().out(type).direct(t,os);
338 return client.futureRead(resp,resp.getOutType());
341 public Future<String> readPost(String pathinfo, String contentType, String ... headers) throws CadiException, APIException {
342 final ParsePath pp = new ParsePath(pathinfo);
344 EClient<CT> client = client();
345 client.setMethod(POST);
346 client.addHeader(CONTENT_TYPE,contentType);
347 client.setPathInfo(pp.path());
348 client.setQueryParams(pp.query());
349 client.setFragment(pp.frag());
350 client.setPayload(new EClient.Transfer() {
352 public void transfer(OutputStream os) throws IOException, APIException {
355 return client.futureReadString();
358 public Future<String> read(String pathinfo, String accept, String ... headers) throws APIException, CadiException {
359 final ParsePath pp = new ParsePath(pathinfo);
361 EClient<CT> client = client();
362 client.setMethod(GET);
363 client.addHeader(ACCEPT, accept);
365 for(int i=1;i<headers.length;i=i+2) {
366 client.addHeader(headers[i-1],headers[i]);
368 client.setPathInfo(pp.path());
369 client.setQueryParams(pp.query());
370 client.setFragment(pp.frag());
371 client.setPayload(null);
373 return client.futureReadString();
376 public<T> Future<T> read(String pathinfo, String accept, RosettaDF<T> df, String ... headers) throws APIException, CadiException {
377 final ParsePath pp = new ParsePath(pathinfo);
379 EClient<CT> client = client();
380 client.setMethod(GET);
381 client.addHeader(ACCEPT, accept);
382 for(int i=1;i<headers.length;i=i+2) {
383 client.addHeader(headers[i-1],headers[i]);
385 client.setPathInfo(pp.path());
386 client.setQueryParams(pp.query());
387 client.setFragment(pp.frag());
388 client.setPayload(null);
390 return client.futureRead(df,type);
393 public<T> Future<T> read(String pathinfo, RosettaDF<T> df,String ... headers) throws APIException, CadiException {
394 final ParsePath pp = new ParsePath(pathinfo);
396 EClient<CT> client = client();
397 client.setMethod(GET);
398 client.addHeader(ACCEPT, typeString(df.getTypeClass()));
399 for(int i=1;i<headers.length;i=i+2) {
400 client.addHeader(headers[i-1],headers[i]);
402 client.setPathInfo(pp.path());
403 client.setQueryParams(pp.query());
404 client.setFragment(pp.frag());
406 client.setPayload(null);
408 return client.futureRead(df,type);
411 public<T> Future<T> read(String pathinfo, Class<?> cls, RosettaDF<T> df) throws APIException, CadiException {
412 final ParsePath pp = new ParsePath(pathinfo);
414 EClient<CT> client = client();
415 client.setMethod(GET);
416 client.addHeader(ACCEPT, typeString(cls));
417 client.setPathInfo(pp.path());
418 client.setQueryParams(pp.query());
419 client.setFragment(pp.frag());
421 client.setPayload(null);
423 return client.futureRead(df,type);
426 public<T> Future<T> update(String pathinfo, String contentType, final RosettaDF<T> df, final T t) throws APIException, CadiException {
427 final ParsePath pp = new ParsePath(pathinfo);
429 EClient<CT> client = client();
430 client.setMethod(PUT);
431 client.addHeader(CONTENT_TYPE,contentType);
432 client.setPathInfo(pp.path());
433 client.setQueryParams(pp.query());
434 client.setFragment(pp.frag());
435 client.setPayload(new EClient.Transfer() {
437 public void transfer(OutputStream os) throws IOException, APIException {
438 df.newData().out(type).direct(t,os);
442 return client.future(t);
445 public<T> Future<String> updateRespondString(String pathinfo, final RosettaDF<T> df, final T t) throws APIException, CadiException {
446 final ParsePath pp = new ParsePath(pathinfo);
448 EClient<CT> client = client();
449 client.setMethod(PUT);
450 client.addHeader(CONTENT_TYPE, typeString(df.getTypeClass()));
451 client.setPathInfo(pp.path());
452 client.setQueryParams(pp.query());
453 client.setFragment(pp.frag());
455 client.setPayload(new EClient.Transfer() {
457 public void transfer(OutputStream os) throws IOException, APIException {
458 //String s = df.newData().out(type).load(t).asString();
459 df.newData().out(type).direct(t,os);
463 return client.futureReadString();
467 public<T> Future<T> update(String pathinfo, final RosettaDF<T> df, final T t) throws APIException, CadiException {
468 final ParsePath pp = new ParsePath(pathinfo);
470 EClient<CT> client = client();
471 client.setMethod(PUT);
472 client.addHeader(CONTENT_TYPE, typeString(df.getTypeClass()));
473 client.setPathInfo(pp.path());
474 client.setQueryParams(pp.query());
475 client.setFragment(pp.frag());
477 client.setPayload(new EClient.Transfer() {
479 public void transfer(OutputStream os) throws IOException, APIException {
480 df.newData().out(type).direct(t,os);
484 return client.future(t);
487 public<T> Future<T> update(String pathinfo, Class<?> cls, final RosettaDF<T> df, final T t) throws APIException, CadiException {
488 final ParsePath pp = new ParsePath(pathinfo);
490 EClient<CT> client = client();
491 client.setMethod(PUT);
492 client.addHeader(CONTENT_TYPE, typeString(cls));
493 client.setPathInfo(pp.path());
494 client.setQueryParams(pp.query());
495 client.setFragment(pp.frag());
497 client.setPayload(new EClient.Transfer() {
499 public void transfer(OutputStream os) throws IOException, APIException {
500 df.newData().out(type).direct(t,os);
504 return client.future(t);
508 * A method to update with a VOID
513 * @throws APIException
514 * @throws CadiException
516 public<T> Future<Void> update(String pathinfo) throws APIException, CadiException {
517 final ParsePath pp = new ParsePath(pathinfo);
519 EClient<CT> client = client();
520 client.setMethod(PUT);
521 client.addHeader(CONTENT_TYPE, typeString(Void.class));
522 client.setPathInfo(pp.path());
523 client.setQueryParams(pp.query());
524 client.setFragment(pp.frag());
526 // client.setPayload(new EClient.Transfer() {
528 // public void transfer(OutputStream os) throws IOException, APIException {
532 return client.future(null);
535 public<T> Future<T> delete(String pathinfo, String contentType, final RosettaDF<T> df, final T t) throws APIException, CadiException {
536 final ParsePath pp = new ParsePath(pathinfo);
538 EClient<CT> client = client();
539 client.setMethod(DELETE);
540 client.addHeader(CONTENT_TYPE, contentType);
541 client.setPathInfo(pp.path());
542 client.setQueryParams(pp.query());
543 client.setFragment(pp.frag());
545 client.setPayload(new EClient.Transfer() {
547 public void transfer(OutputStream os) throws IOException, APIException {
548 df.newData().out(type).direct(t,os);
552 return client.future(t);
555 public<T> Future<T> delete(String pathinfo, Class<?> cls, final RosettaDF<T> df, final T t) throws APIException, CadiException {
556 final ParsePath pp = new ParsePath(pathinfo);
558 EClient<CT> client = client();
559 client.setMethod(DELETE);
560 client.addHeader(CONTENT_TYPE, typeString(cls));
561 client.setPathInfo(pp.path());
562 client.setQueryParams(pp.query());
563 client.setFragment(pp.frag());
564 client.setPayload(new EClient.Transfer() {
566 public void transfer(OutputStream os) throws IOException, APIException {
567 df.newData().out(type).direct(t,os);
571 return client.future(t);
574 public<T> Future<T> delete(String pathinfo, final RosettaDF<T> df, final T t) throws APIException, CadiException {
575 final ParsePath pp = new ParsePath(pathinfo);
577 EClient<CT> client = client();
578 client.setMethod(DELETE);
579 client.addHeader(CONTENT_TYPE, typeString(df.getTypeClass()));
580 client.setPathInfo(pp.path());
581 client.setQueryParams(pp.query());
582 client.setFragment(pp.frag());
583 client.setPayload(new EClient.Transfer() {
585 public void transfer(OutputStream os) throws IOException, APIException {
586 df.newData().out(type).direct(t,os);
591 return client.future(t);
595 public<T> Future<T> delete(String pathinfo, Class<T> cls) throws APIException, CadiException {
596 final ParsePath pp = new ParsePath(pathinfo);
598 EClient<CT> client = client();
599 client.setMethod(DELETE);
600 client.addHeader(CONTENT_TYPE, typeString(cls));
601 client.setPathInfo(pp.path());
602 client.setQueryParams(pp.query());
603 client.setFragment(pp.frag());
605 client.setPayload(null);
607 return client.future((T)null);
610 public Future<Void> delete(String pathinfo, String contentType) throws APIException, CadiException {
611 final ParsePath pp = new ParsePath(pathinfo);
613 EClient<CT> client = client();
614 client.setMethod(DELETE);
615 client.addHeader(CONTENT_TYPE, contentType);
616 client.setPathInfo(pp.path());
617 client.setQueryParams(pp.query());
618 client.setFragment(pp.frag());
620 client.setPayload(null);
622 return client.future(null);
625 public Future<Void> transfer(final HttpServletRequest req, final HttpServletResponse resp, final String pathParam, final int expected) throws CadiException, APIException {
626 EClient<CT> client = client();
629 uri = new URI(req.getRequestURI());
630 } catch (Exception e) {
631 throw new CadiException("Invalid incoming URI",e);
634 for(Enumeration<String> en = req.getHeaderNames();en.hasMoreElements();) {
635 name = en.nextElement();
636 client.addHeader(name,req.getHeader(name));
638 client.setQueryParams(req.getQueryString());
639 client.setFragment(uri.getFragment());
640 client.setPathInfo(pathParam);
641 String meth = req.getMethod();
642 client.setMethod(meth);
643 if(!"GET".equals(meth)) {
644 client.setPayload(new EClient.Transfer() {
646 public void transfer(OutputStream os) throws IOException, APIException {
647 final ServletInputStream is = req.getInputStream();
650 Pooled<byte[]> pbuff = buffPool.get();
652 while((read=is.read(pbuff.content))>=0) {
653 os.write(pbuff.content,0,read);
662 return client.future(resp, expected);
665 private class ParsePath {
666 private final String path;
667 private final int query;
668 private final int queryEnd;
669 private final int pound;
670 private final String queryParams;
672 public ParsePath(final String origPath) {
675 query=queryEnd=pound=-1;
678 query = origPath.indexOf('?');
679 pound = origPath.indexOf('#');
680 queryEnd = pound>=0?pound:path.length();
681 if(oneCallQueryParams==null) {
683 queryParams = path.substring(query+1,queryEnd);
689 queryParams = oneCallQueryParams + '&' + path.substring(query+1,queryEnd);
691 queryParams = oneCallQueryParams;
693 oneCallQueryParams = null;
698 public String path() {
701 return path.substring(pound+1);
703 return path.substring(0,query);
704 } else if(pound>=0) {
705 return path.substring(0,pound);
711 public String query() {
715 public String frag() {
717 return path.substring(pound+1);
724 public String toString() {
725 return uri.toString();
728 public URI getURI() {
732 public void setQueryParams(final String queryParams) {
733 oneCallQueryParams=queryParams;