6b486b8e22340e65d24ca9e96adf54c50c8906c0
[aaf/authz.git] / cadi / client / src / main / java / org / onap / aaf / cadi / client / Rcli.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.client;
23
24 import java.io.IOException;
25 import java.io.OutputStream;
26 import java.io.PrintStream;
27 import java.net.URI;
28 import java.util.Enumeration;
29
30 import javax.servlet.ServletInputStream;
31 import javax.servlet.http.HttpServletRequest;
32 import javax.servlet.http.HttpServletResponse;
33
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;
42
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";
50
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";
55     protected TYPE type;
56     protected String apiVersion;
57     protected int readTimeout = 5000;
58     protected int connectionTimeout = 3000;
59     protected URI uri;
60     private String oneCallQueryParams;
61     public static Pool<byte[]> buffPool = new Pool<byte[]>(new Pool.Creator<byte[]>() {
62         @Override
63         public byte[] create() throws APIException {
64             return new byte[1024];
65         }
66
67         @Override
68         public void destroy(byte[] t) {
69         }
70
71         @Override
72         public boolean isValid(byte[] t) {
73             return true;
74         }
75
76         @Override
77         public void reuse(byte[] t) {
78         }
79     });
80
81
82     public Rcli() {
83         super();
84     }
85
86     public abstract void setSecuritySetter(SecuritySetter<CT> ss);
87     public abstract SecuritySetter<CT> getSecuritySetter();
88
89
90     public Rcli<CT> forUser(SecuritySetter<CT> ss) {
91         Rcli<CT> rv = clone(uri==null?this.uri:uri,ss);
92         setSecuritySetter(ss);
93         rv.type = type;
94         rv.apiVersion = apiVersion;
95         return rv;
96     }
97     
98     protected abstract Rcli<CT> clone(URI uri, SecuritySetter<CT> ss);
99     
100     public abstract void invalidate() throws CadiException;
101
102     public Rcli<CT> readTimeout(int millis) {
103         readTimeout = millis;
104         return this;
105     }
106
107     public Rcli<CT> connectionTimeout(int millis) {
108         connectionTimeout = millis;
109         return this;
110     }
111
112     public Rcli<CT> type(TYPE type) {
113         this.type=type;
114         return this;
115     }
116
117     public Rcli<CT> apiVersion(String apiVersion) {
118         this.apiVersion = apiVersion;
119         return this;
120     }
121     
122     public boolean isApiVersion(String prospective) {
123         return apiVersion.equals(prospective);
124     }
125
126
127     public String typeString(Class<?> cls) {
128         return "application/"+cls.getSimpleName()+"+"+type.name().toLowerCase()+
129                 (apiVersion==null?BLANK:";version="+apiVersion);
130     }
131
132     protected abstract EClient<CT> client() throws CadiException;
133
134
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);
137
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() {
145             @Override
146             public void transfer(OutputStream os) throws IOException, APIException {
147                 df.newData().out(type).direct(t,os);
148             }
149         });
150         client.send();
151         return client.futureCreate(df.getTypeClass());
152     }
153
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);
156
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() {
164             @Override
165             public void transfer(OutputStream os) throws IOException, APIException {
166                 df.newData().out(type).direct(t,os);
167             }
168         });
169         client.send();
170         return client.futureCreate(df.getTypeClass());
171     }
172
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);
175
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() {
183             @Override
184             public void transfer(OutputStream os) throws IOException, APIException {
185                 df.newData().out(type).direct(t,os);
186             }
187         });
188         client.send();
189         return client.futureCreate(df.getTypeClass());
190     }
191
192     public<T> Future<T> create(String pathinfo, Class<T> cls) throws APIException, CadiException {
193         final ParsePath pp = new ParsePath(pathinfo);
194
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);
202         client.send();
203         return client.futureCreate(cls);
204     }
205
206     public Future<Void> create(String pathinfo, String contentType) throws APIException, CadiException {
207         final ParsePath pp = new ParsePath(pathinfo);
208
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);
216         client.send();
217         return client.futureCreate(Void.class);
218     }
219
220     public Future<Void> create(String pathinfo, String contentType, EClient.Transfer content) throws APIException, CadiException {
221         final ParsePath pp = new ParsePath(pathinfo);
222
223         EClient<CT> client = client();
224         client.setMethod(POST);
225         client.addHeader(CONTENT_TYPE,contentType);
226         client.setPathInfo(pp.path());
227         client.setQueryParams(pp.query());
228         client.setFragment(pp.frag());
229         client.setPayload(content);
230         client.send();
231         return client.futureCreate(Void.class);
232     }
233
234
235     /**
236      * Post Data in WWW expected format, with the format tag1=value1&tag2=value2, etc
237      * Note Shortcut:
238      *   Because typically, you will want to have a variable as value, you can type, as long as tag ends with "="
239      *   postForm(..., "tag1=value1","tag2=",var2);
240      * @param pathinfo
241      * @param df
242      * @param cls
243      * @param formParam
244      * @return
245      * @throws APIException
246      * @throws CadiException
247      */
248     public <T> Future<T> postForm(String pathinfo, final RosettaDF<T> df, final String ... formParam) throws APIException, CadiException {
249         final ParsePath pp = new ParsePath(pathinfo);
250
251         EClient<CT> client = client();
252         client.setMethod(POST);
253         client.addHeader(CONTENT_TYPE,FORM_ENCODED);
254         switch(type) {
255             case JSON:
256                 client.addHeader(ACCEPT, APPL_JSON);
257                 break;
258             case XML:
259                 client.addHeader(ACCEPT, APPL_XML);
260                 break;
261             default:
262                 break;
263         }
264         client.setPathInfo(pp.path());
265         client.setQueryParams(pp.query());
266         client.setFragment(pp.frag());
267         client.setPayload(new Transfer() {
268             @Override
269             public void transfer(OutputStream os) throws IOException, APIException {
270                 PrintStream ps;
271                 if (os instanceof PrintStream) {
272                     ps = (PrintStream)os;
273                 } else {
274                     ps = new PrintStream(os);
275                 }
276                 boolean first = true;
277                 for (String fp : formParam) {
278                     if (fp!=null) {
279                         if (first) {
280                             first = false;
281                         } else {
282                             ps.print('&');
283                         }
284                         if (fp.endsWith("=")) {
285                             first = true;
286                         }
287                         ps.print(fp);
288                     }
289                 }
290             }});
291         client.send();
292         return client.futureRead(df,TYPE.JSON);
293     }
294
295     /**
296      * Read String, using POST for keyInfo
297      * 
298      * @param pathinfo
299      * @param df
300      * @param t
301      * @param resp
302      * @return
303      * @throws APIException
304      * @throws CadiException
305      */
306     public<T> Future<String> readPost(String pathinfo, final RosettaDF<T> df, final T t) throws APIException, CadiException {
307         final ParsePath pp = new ParsePath(pathinfo);
308
309         EClient<CT> client = client();
310         client.setMethod(POST);
311         client.addHeader(CONTENT_TYPE,typeString(df.getTypeClass()));
312         client.setPathInfo(pp.path());
313         client.setQueryParams(pp.query());
314         client.setFragment(pp.frag());
315         client.setPayload(new EClient.Transfer() {
316             @Override
317             public void transfer(OutputStream os) throws IOException, APIException {
318                 df.newData().out(type).direct(t,os);
319             }
320         });
321         client.send();
322         return client.futureReadString();
323     }
324
325     /**
326      * Read using POST for keyInfo, responding with marshaled Objects
327      *
328      * @param pathinfo
329      * @param df
330      * @param t
331      * @param resp
332      * @return
333      * @throws APIException
334      * @throws CadiException
335      */
336     public<T,R> Future<R> readPost(String pathinfo, final RosettaDF<T> df, final T t, final RosettaDF<R> resp) throws APIException, CadiException {
337         final ParsePath pp = new ParsePath(pathinfo);
338         
339         EClient<CT> client = client();
340         client.setMethod(POST);
341         client.addHeader(CONTENT_TYPE,typeString(df.getTypeClass()));
342         client.setPathInfo(pp.path());
343         client.setQueryParams(pp.query());
344         client.setFragment(pp.frag());
345         client.setPayload(new EClient.Transfer() {
346             @Override
347             public void transfer(OutputStream os) throws IOException, APIException {
348                 df.newData().out(type).direct(t,os);
349             }
350         });
351         client.send();
352         return client.futureRead(resp,resp.getOutType());
353     }
354
355     public Future<String> readPost(String pathinfo, String contentType, String ... headers) throws CadiException, APIException {
356         final ParsePath pp = new ParsePath(pathinfo);
357
358         EClient<CT> client = client();
359         client.setMethod(POST);
360         client.addHeader(CONTENT_TYPE,contentType);
361         client.setPathInfo(pp.path());
362         client.setQueryParams(pp.query());
363         client.setFragment(pp.frag());
364         client.setPayload(new EClient.Transfer() {
365             @Override
366             public void transfer(OutputStream os) throws IOException, APIException {
367             }});
368         client.send();
369         return client.futureReadString();
370     }
371
372     public Future<String> read(String pathinfo, String accept, String ... headers) throws APIException, CadiException {
373         final ParsePath pp = new ParsePath(pathinfo);
374     
375         EClient<CT> client = client();
376         client.setMethod(GET);
377         client.addHeader(ACCEPT, accept);
378         
379         for (int i=1;i<headers.length;i=i+2) {
380             client.addHeader(headers[i-1],headers[i]);
381         }
382         client.setPathInfo(pp.path());
383         client.setQueryParams(pp.query());
384         client.setFragment(pp.frag());
385         client.setPayload(null);
386         client.send();
387         return client.futureReadString();
388     }
389
390     public<T> Future<T> read(String pathinfo, String accept, RosettaDF<T> df, String ... headers) throws APIException, CadiException {
391         final ParsePath pp = new ParsePath(pathinfo);
392
393         EClient<CT> client = client();
394         client.setMethod(GET);
395         client.addHeader(ACCEPT, accept);
396         for (int i=1;i<headers.length;i=i+2) {
397             client.addHeader(headers[i-1],headers[i]);
398         }
399         client.setPathInfo(pp.path());
400         client.setQueryParams(pp.query());
401         client.setFragment(pp.frag());
402         client.setPayload(null);
403         client.send();
404         return client.futureRead(df,type);
405     }
406
407     public<T> Future<T> read(String pathinfo, RosettaDF<T> df,String ... headers) throws APIException, CadiException {
408         final ParsePath pp = new ParsePath(pathinfo);
409
410         EClient<CT> client = client();
411         client.setMethod(GET);
412         client.addHeader(ACCEPT, typeString(df.getTypeClass()));
413         for (int i=1;i<headers.length;i=i+2) {
414             client.addHeader(headers[i-1],headers[i]);
415         }
416         client.setPathInfo(pp.path());
417         client.setQueryParams(pp.query());
418         client.setFragment(pp.frag());
419         
420         client.setPayload(null);
421         client.send();
422         return client.futureRead(df,type);
423     }
424
425     public<T> Future<T> read(String pathinfo, Class<?> cls, RosettaDF<T> df) throws APIException, CadiException {
426         final ParsePath pp = new ParsePath(pathinfo);
427
428         EClient<CT> client = client();
429         client.setMethod(GET);
430         client.addHeader(ACCEPT, typeString(cls));
431         client.setPathInfo(pp.path());
432         client.setQueryParams(pp.query());
433         client.setFragment(pp.frag());        
434
435         client.setPayload(null);
436         client.send();
437         return client.futureRead(df,type);
438     }
439
440     public<T> Future<T> update(String pathinfo, String contentType, final RosettaDF<T> df, final T t) throws APIException, CadiException {
441         final ParsePath pp = new ParsePath(pathinfo);
442
443         EClient<CT> client = client();
444         client.setMethod(PUT);
445         client.addHeader(CONTENT_TYPE,contentType);
446         client.setPathInfo(pp.path());
447         client.setQueryParams(pp.query());
448         client.setFragment(pp.frag());        
449         client.setPayload(new EClient.Transfer() {
450             @Override
451             public void transfer(OutputStream os) throws IOException, APIException {
452                 df.newData().out(type).direct(t,os);
453             }
454         });
455         client.send();
456         return client.future(t);
457     }
458     
459     public<T> Future<String> updateRespondString(String pathinfo, final RosettaDF<T> df, final T t) throws APIException, CadiException {
460         final ParsePath pp = new ParsePath(pathinfo);
461         
462         EClient<CT> client = client();
463         client.setMethod(PUT);
464         client.addHeader(CONTENT_TYPE, typeString(df.getTypeClass()));
465         client.setPathInfo(pp.path());
466         client.setQueryParams(pp.query());
467         client.setFragment(pp.frag());        
468
469         client.setPayload(new EClient.Transfer() {
470             @Override
471             public void transfer(OutputStream os) throws IOException, APIException {
472                 //String s = df.newData().out(type).load(t).asString();
473                 df.newData().out(type).direct(t,os);
474             }
475         });
476         client.send();
477         return client.futureReadString();
478     }
479     
480     public Future<String> update(String pathinfo, String contentType, EClient.Transfer content) throws APIException, CadiException {
481         final ParsePath pp = new ParsePath(pathinfo);
482
483         EClient<CT> client = client();
484         client.setMethod(PUT);
485         client.addHeader(CONTENT_TYPE,contentType);
486         client.setPathInfo(pp.path());
487         client.setQueryParams(pp.query());
488         client.setFragment(pp.frag());
489         client.setPayload(content);
490         client.send();
491         return client.futureReadString();
492     }
493
494
495
496
497     public<T> Future<T> update(String pathinfo, final RosettaDF<T> df, final T t) throws APIException, CadiException {
498         final ParsePath pp = new ParsePath(pathinfo);
499
500         EClient<CT> client = client();
501         client.setMethod(PUT);
502         client.addHeader(CONTENT_TYPE, typeString(df.getTypeClass()));
503         client.setPathInfo(pp.path());
504         client.setQueryParams(pp.query());
505         client.setFragment(pp.frag());    
506         
507         client.setPayload(new EClient.Transfer() {
508             @Override
509             public void transfer(OutputStream os) throws IOException, APIException {
510                 df.newData().out(type).direct(t,os);
511             }
512         });
513         client.send();
514         return client.future(t);
515     }
516     
517     public<T> Future<T> update(String pathinfo, Class<?> cls, final RosettaDF<T> df, final T t) throws APIException, CadiException {
518         final ParsePath pp = new ParsePath(pathinfo);
519         
520         EClient<CT> client = client();
521         client.setMethod(PUT);
522         client.addHeader(CONTENT_TYPE, typeString(cls));
523         client.setPathInfo(pp.path());
524         client.setQueryParams(pp.query());
525         client.setFragment(pp.frag());    
526
527         client.setPayload(new EClient.Transfer() {
528             @Override
529             public void transfer(OutputStream os) throws IOException, APIException {
530                 df.newData().out(type).direct(t,os);
531             }
532         });
533         client.send();
534         return client.future(t);
535     }
536     
537
538
539     /**
540      * A method to update with a VOID
541      * @param pathinfo
542      * @param resp
543      * @param expected
544      * @return
545      * @throws APIException
546      * @throws CadiException
547      */
548     public<T> Future<Void> update(String pathinfo) throws APIException, CadiException {
549         final ParsePath pp = new ParsePath(pathinfo);
550
551         EClient<CT> client = client();
552         client.setMethod(PUT);
553         client.addHeader(CONTENT_TYPE, typeString(Void.class));
554         client.setPathInfo(pp.path());
555         client.setQueryParams(pp.query());
556         client.setFragment(pp.frag());    
557
558 //        client.setPayload(new EClient.Transfer() {
559 //            @Override
560 //            public void transfer(OutputStream os) throws IOException, APIException {
561 //            }
562 //        });
563         client.send();
564         return client.future(null);
565     }
566     
567
568     public<T> Future<T> delete(String pathinfo, String contentType, final RosettaDF<T> df, final T t) throws APIException, CadiException {
569         final ParsePath pp = new ParsePath(pathinfo);
570
571         EClient<CT> client = client();
572         client.setMethod(DELETE);
573         client.addHeader(CONTENT_TYPE, contentType);
574         client.setPathInfo(pp.path());
575         client.setQueryParams(pp.query());
576         client.setFragment(pp.frag());    
577
578         client.setPayload(new EClient.Transfer() {
579             @Override
580             public void transfer(OutputStream os) throws IOException, APIException {
581                 df.newData().out(type).direct(t,os);
582             }
583         });
584         client.send();
585         return client.future(t);
586     }
587
588     public<T> Future<T> delete(String pathinfo, Class<?> cls, final RosettaDF<T> df, final T t) throws APIException, CadiException {
589         final ParsePath pp = new ParsePath(pathinfo);
590
591         EClient<CT> client = client();
592         client.setMethod(DELETE);
593         client.addHeader(CONTENT_TYPE, typeString(cls));
594         client.setPathInfo(pp.path());
595         client.setQueryParams(pp.query());
596         client.setFragment(pp.frag());    
597         client.setPayload(new EClient.Transfer() {
598             @Override
599             public void transfer(OutputStream os) throws IOException, APIException {
600                 df.newData().out(type).direct(t,os);
601             }
602         });
603         client.send();
604         return client.future(t);
605     }
606
607     public<T> Future<T> delete(String pathinfo, final RosettaDF<T> df, final T t) throws APIException, CadiException {
608         final ParsePath pp = new ParsePath(pathinfo);
609
610         EClient<CT> client = client();
611         client.setMethod(DELETE);
612         client.addHeader(CONTENT_TYPE, typeString(df.getTypeClass()));
613         client.setPathInfo(pp.path());
614         client.setQueryParams(pp.query());
615         client.setFragment(pp.frag());    
616         client.setPayload(new EClient.Transfer() {
617             @Override
618             public void transfer(OutputStream os) throws IOException, APIException {
619                 df.newData().out(type).direct(t,os);
620             }
621         });
622
623         client.send();
624         return client.future(t);
625     }
626
627
628     public<T> Future<T> delete(String pathinfo, Class<T> cls) throws APIException, CadiException {
629         final ParsePath pp = new ParsePath(pathinfo);
630
631         EClient<CT> client = client();
632         client.setMethod(DELETE);
633         client.addHeader(CONTENT_TYPE, typeString(cls));
634         client.setPathInfo(pp.path());
635         client.setQueryParams(pp.query());
636         client.setFragment(pp.frag());    
637
638         client.setPayload(null);
639         client.send();
640         return client.future((T)null);
641     }
642
643     public Future<Void> delete(String pathinfo, String contentType) throws APIException, CadiException {
644         final ParsePath pp = new ParsePath(pathinfo);
645
646         EClient<CT> client = client();
647         client.setMethod(DELETE);
648         client.addHeader(CONTENT_TYPE, contentType);
649         client.setPathInfo(pp.path());
650         client.setQueryParams(pp.query());
651         client.setFragment(pp.frag());    
652
653         client.setPayload(null);
654         client.send();
655         return client.future(null);
656     }
657
658     public Future<Void> transfer(final HttpServletRequest req, final HttpServletResponse resp, final String pathParam, final int expected) throws CadiException, APIException {
659         EClient<CT> client = client();
660         URI uri;
661         try {
662             uri = new URI(req.getRequestURI());
663         } catch (Exception e) {
664             throw new CadiException("Invalid incoming URI",e);
665         }
666         String name;
667         for (Enumeration<String> en = req.getHeaderNames();en.hasMoreElements();) {
668             name = en.nextElement();
669             client.addHeader(name,req.getHeader(name));
670         }
671         client.setQueryParams(req.getQueryString());
672         client.setFragment(uri.getFragment());
673         client.setPathInfo(pathParam);
674         String meth = req.getMethod();
675         client.setMethod(meth);
676         if (!"GET".equals(meth)) {
677             client.setPayload(new EClient.Transfer() {
678                 @Override
679                 public void transfer(OutputStream os) throws IOException, APIException {
680                     final ServletInputStream is = req.getInputStream();
681                     int read;
682                     // reuse Buffers
683                     Pooled<byte[]> pbuff = buffPool.get();
684                     try { 
685                         while ((read=is.read(pbuff.content))>=0) {
686                             os.write(pbuff.content,0,read);
687                         }
688                     } finally {
689                         pbuff.done();
690                     }
691                 }
692             });
693         }
694         client.send();
695         return client.future(resp, expected);
696     }
697
698     private class ParsePath {
699         private final String path;
700         private final int query;
701         private final int queryEnd;
702         private final int pound;
703         private final String queryParams;
704
705         public ParsePath(final String origPath) {
706             path = origPath;
707             if (origPath==null) {
708                 query=queryEnd=pound=-1;
709                 queryParams=null;
710             } else {
711                 query = origPath.indexOf('?');
712                 pound = origPath.indexOf('#');
713                 queryEnd = pound>=0?pound:path.length();
714                 if (oneCallQueryParams==null) {
715                     if (query>=0) {
716                         queryParams = path.substring(query+1,queryEnd); 
717                     } else {
718                         queryParams=null;
719                     }
720                 } else {
721                     if (query>=0) {
722                         queryParams = oneCallQueryParams + '&' + path.substring(query+1,queryEnd); 
723                     } else {
724                         queryParams = oneCallQueryParams;
725                     }
726                     oneCallQueryParams = null;
727                 }
728             }
729         }
730         
731         public String path() {
732             if (query>=0) {
733                 if (pound>=0) {
734                     return path.substring(pound+1);
735                 }
736                 return path.substring(0,query);
737             } else if (pound>=0) {
738                 return path.substring(0,pound);
739             } else {
740                 return path;
741             }
742         }
743         
744         public String query() {
745             return queryParams;
746         }
747         
748         public String frag() {
749             if (pound>=0) {
750                 return path.substring(pound+1);
751             } else {
752                 return null;
753             }
754         }
755     }
756
757     public String toString() {
758         return uri.toString();
759     }
760
761     public URI getURI() {
762         return uri;
763     }
764
765     public void setQueryParams(final String queryParams) {
766         oneCallQueryParams=queryParams;
767     }
768
769 }