Refine Agent for cadi utils
[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
221         /**
222          * Post Data in WWW expected format, with the format tag1=value1&tag2=value2, etc
223          * Note Shortcut:
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);
226          * @param pathinfo
227          * @param df
228          * @param cls
229          * @param formParam
230          * @return
231          * @throws APIException
232          * @throws CadiException
233          */
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);
236
237                 EClient<CT> client = client();
238                 client.setMethod(POST);
239                 client.addHeader(CONTENT_TYPE,FORM_ENCODED);
240                 switch(type) {
241                         case JSON:
242                                 client.addHeader(ACCEPT, APPL_JSON);
243                                 break;
244                         case XML:
245                                 client.addHeader(ACCEPT, APPL_XML);
246                                 break;
247                         default:
248                                 break;
249                 }
250                 client.setPathInfo(pp.path());
251                 client.setQueryParams(pp.query());
252                 client.setFragment(pp.frag());
253                 client.setPayload(new Transfer() {
254                         @Override
255                         public void transfer(OutputStream os) throws IOException, APIException {
256                                 PrintStream ps;
257                                 if(os instanceof PrintStream) {
258                                         ps = (PrintStream)os;
259                                 } else {
260                                         ps = new PrintStream(os);
261                                 }
262                                 boolean first = true;
263                                 for(String fp : formParam) {
264                                         if(fp!=null) {
265                                                 if(first) {
266                                                         first = false;
267                                                 } else {
268                                                         ps.print('&');
269                                                 }
270                                                 if(fp.endsWith("=")) {
271                                                         first = true;
272                                                 }
273                                                 ps.print(fp);
274                                         }
275                                 }
276                         }});
277                 client.send();
278                 return client.futureRead(df,TYPE.JSON);
279         }
280
281         /**
282          * Read String, using POST for keyInfo
283          * 
284          * @param pathinfo
285          * @param df
286          * @param t
287          * @param resp
288          * @return
289          * @throws APIException
290          * @throws CadiException
291          */
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);
294
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() {
302                         @Override
303                         public void transfer(OutputStream os) throws IOException, APIException {
304                                 df.newData().out(type).direct(t,os);
305                         }
306                 });
307                 client.send();
308                 return client.futureReadString();
309         }
310
311         /**
312          * Read using POST for keyInfo, responding with marshaled Objects
313          *
314          * @param pathinfo
315          * @param df
316          * @param t
317          * @param resp
318          * @return
319          * @throws APIException
320          * @throws CadiException
321          */
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);
324                 
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() {
332                         @Override
333                         public void transfer(OutputStream os) throws IOException, APIException {
334                                 df.newData().out(type).direct(t,os);
335                         }
336                 });
337                 client.send();
338                 return client.futureRead(resp,resp.getOutType());
339         }
340
341         public Future<String> readPost(String pathinfo, String contentType, String ... headers) throws CadiException, APIException {
342                 final ParsePath pp = new ParsePath(pathinfo);
343
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() {
351                         @Override
352                         public void transfer(OutputStream os) throws IOException, APIException {
353                         }});
354                 client.send();
355                 return client.futureReadString();
356         }
357
358         public Future<String> read(String pathinfo, String accept, String ... headers) throws APIException, CadiException {
359                 final ParsePath pp = new ParsePath(pathinfo);
360         
361                 EClient<CT> client = client();
362                 client.setMethod(GET);
363                 client.addHeader(ACCEPT, accept);
364                 
365                 for(int i=1;i<headers.length;i=i+2) {
366                         client.addHeader(headers[i-1],headers[i]);
367                 }
368                 client.setPathInfo(pp.path());
369                 client.setQueryParams(pp.query());
370                 client.setFragment(pp.frag());
371                 client.setPayload(null);
372                 client.send();
373                 return client.futureReadString();
374         }
375
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);
378
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]);
384                 }
385                 client.setPathInfo(pp.path());
386                 client.setQueryParams(pp.query());
387                 client.setFragment(pp.frag());
388                 client.setPayload(null);
389                 client.send();
390                 return client.futureRead(df,type);
391         }
392
393         public<T> Future<T> read(String pathinfo, RosettaDF<T> df,String ... headers) throws APIException, CadiException {
394                 final ParsePath pp = new ParsePath(pathinfo);
395
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]);
401                 }
402                 client.setPathInfo(pp.path());
403                 client.setQueryParams(pp.query());
404                 client.setFragment(pp.frag());
405                 
406                 client.setPayload(null);
407                 client.send();
408                 return client.futureRead(df,type);
409         }
410
411         public<T> Future<T> read(String pathinfo, Class<?> cls, RosettaDF<T> df) throws APIException, CadiException {
412                 final ParsePath pp = new ParsePath(pathinfo);
413
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());          
420
421                 client.setPayload(null);
422                 client.send();
423                 return client.futureRead(df,type);
424         }
425
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);
428
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() {
436                         @Override
437                         public void transfer(OutputStream os) throws IOException, APIException {
438                                 df.newData().out(type).direct(t,os);
439                         }
440                 });
441                 client.send();
442                 return client.future(t);
443         }
444         
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);
447                 
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());          
454
455                 client.setPayload(new EClient.Transfer() {
456                         @Override
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);
460                         }
461                 });
462                 client.send();
463                 return client.futureReadString();
464         }
465
466
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);
469
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());  
476                 
477                 client.setPayload(new EClient.Transfer() {
478                         @Override
479                         public void transfer(OutputStream os) throws IOException, APIException {
480                                 df.newData().out(type).direct(t,os);
481                         }
482                 });
483                 client.send();
484                 return client.future(t);
485         }
486         
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);
489                 
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());  
496
497                 client.setPayload(new EClient.Transfer() {
498                         @Override
499                         public void transfer(OutputStream os) throws IOException, APIException {
500                                 df.newData().out(type).direct(t,os);
501                         }
502                 });
503                 client.send();
504                 return client.future(t);
505         }
506
507         /**
508          * A method to update with a VOID
509          * @param pathinfo
510          * @param resp
511          * @param expected
512          * @return
513          * @throws APIException
514          * @throws CadiException
515          */
516         public<T> Future<Void> update(String pathinfo) throws APIException, CadiException {
517                 final ParsePath pp = new ParsePath(pathinfo);
518
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());  
525
526 //              client.setPayload(new EClient.Transfer() {
527 //                      @Override
528 //                      public void transfer(OutputStream os) throws IOException, APIException {
529 //                      }
530 //              });
531                 client.send();
532                 return client.future(null);
533         }
534
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);
537
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());  
544
545                 client.setPayload(new EClient.Transfer() {
546                         @Override
547                         public void transfer(OutputStream os) throws IOException, APIException {
548                                 df.newData().out(type).direct(t,os);
549                         }
550                 });
551                 client.send();
552                 return client.future(t);
553         }
554
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);
557
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() {
565                         @Override
566                         public void transfer(OutputStream os) throws IOException, APIException {
567                                 df.newData().out(type).direct(t,os);
568                         }
569                 });
570                 client.send();
571                 return client.future(t);
572         }
573
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);
576
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() {
584                         @Override
585                         public void transfer(OutputStream os) throws IOException, APIException {
586                                 df.newData().out(type).direct(t,os);
587                         }
588                 });
589
590                 client.send();
591                 return client.future(t);
592         }
593
594
595         public<T> Future<T> delete(String pathinfo, Class<T> cls) throws APIException, CadiException {
596                 final ParsePath pp = new ParsePath(pathinfo);
597
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());  
604
605                 client.setPayload(null);
606                 client.send();
607                 return client.future((T)null);
608         }
609
610         public Future<Void> delete(String pathinfo, String contentType) throws APIException, CadiException {
611                 final ParsePath pp = new ParsePath(pathinfo);
612
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());  
619
620                 client.setPayload(null);
621                 client.send();
622                 return client.future(null);
623         }
624
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();
627                 URI uri;
628                 try {
629                         uri = new URI(req.getRequestURI());
630                 } catch (Exception e) {
631                         throw new CadiException("Invalid incoming URI",e);
632                 }
633                 String name;
634                 for(Enumeration<String> en = req.getHeaderNames();en.hasMoreElements();) {
635                         name = en.nextElement();
636                         client.addHeader(name,req.getHeader(name));
637                 }
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() {
645                                 @Override
646                                 public void transfer(OutputStream os) throws IOException, APIException {
647                                         final ServletInputStream is = req.getInputStream();
648                                         int read;
649                                         // reuse Buffers
650                                         Pooled<byte[]> pbuff = buffPool.get();
651                                         try { 
652                                                 while((read=is.read(pbuff.content))>=0) {
653                                                         os.write(pbuff.content,0,read);
654                                                 }
655                                         } finally {
656                                                 pbuff.done();
657                                         }
658                                 }
659                         });
660                 }
661                 client.send();
662                 return client.future(resp, expected);
663         }
664
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;
671
672                 public ParsePath(final String origPath) {
673                         path = origPath;
674                         if(origPath==null) {
675                                 query=queryEnd=pound=-1;
676                                 queryParams=null;
677                         } else {
678                                 query = origPath.indexOf('?');
679                                 pound = origPath.indexOf('#');
680                                 queryEnd = pound>=0?pound:path.length();
681                                 if(oneCallQueryParams==null) {
682                                         if(query>=0) {
683                                                 queryParams = path.substring(query+1,queryEnd); 
684                                         } else {
685                                                 queryParams=null;
686                                         }
687                                 } else {
688                                         if(query>=0) {
689                                                 queryParams = oneCallQueryParams + '&' + path.substring(query+1,queryEnd); 
690                                         } else {
691                                                 queryParams = oneCallQueryParams;
692                                         }
693                                         oneCallQueryParams = null;
694                                 }
695                         }
696                 }
697                 
698                 public String path() {
699                         if(query>=0) {
700                                 if(pound>=0) {
701                                         return path.substring(pound+1);
702                                 }
703                                 return path.substring(0,query);
704                         } else if(pound>=0) {
705                                 return path.substring(0,pound);
706                         } else {
707                                 return path;
708                         }
709                 }
710                 
711                 public String query() {
712                         return queryParams;
713                 }
714                 
715                 public String frag() {
716                         if(pound>=0) {
717                                 return path.substring(pound+1);
718                         } else {
719                                 return null;
720                         }
721                 }
722         }
723
724         public String toString() {
725                 return uri.toString();
726         }
727
728         public URI getURI() {
729                 return uri;
730         }
731
732         public void setQueryParams(final String queryParams) {
733                 oneCallQueryParams=queryParams;
734         }
735
736 }