AT&T 2.0.19 Code drop, stage 3
[aaf/authz.git] / auth / auth-core / src / main / java / org / onap / aaf / auth / rserv / TypedCode.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.auth.rserv;
23
24 import java.io.IOException;
25 import java.util.ArrayList;
26 import java.util.HashMap;
27 import java.util.List;
28
29 import javax.servlet.ServletException;
30
31 import org.onap.aaf.misc.env.Env;
32 import org.onap.aaf.misc.env.TimeTaken;
33 import org.onap.aaf.misc.env.Trans;
34
35
36 /**
37  * TypedCode organizes implementation code based on the Type and Version of code it works with so that it can
38  * be located quickly at runtime based on the "Accept" HTTP Header.
39  *
40  * FYI: For those in the future wondering why I would create a specialized set of "Pair" for the data content:
41  *   1) TypeCode is used in Route, and this code is used for every transaction... it needs to be blazingly fast
42  *   2) The actual number of objects accessed is quite small and built at startup.  Arrays are best
43  *   3) I needed a small, well defined tree where each level is a different Type.  Using a "Pair" Generic definitions, 
44  *      I created type-safety at each level, which you can't get from a TreeSet, etc.
45  *   4) Chaining through the Network is simply object dereferencing, which is as fast as Java can go.
46  *   5) The drawback is that in your code is that all the variables are named "x" and "y", which can be a bit hard to
47  *      read both in code, and in the debugger.  However, TypeSafety allows your IDE (Eclipse) to help you make the 
48  *      choices.  Also, make sure you have a good "toString()" method on each object so you can see what's happening
49  *      in the IDE Debugger.
50  *   
51  * Empirically, this method of obtaining routes proved to be much faster than the HashSet implementations available in otherwise
52  * competent Open Source.
53  *   
54  * @author Jonathan
55  *
56  * @param <TRANS>
57  */
58 public class TypedCode<TRANS extends Trans> extends Content<TRANS> {
59                 private List<Pair<String, Pair<HttpCode<TRANS,?>,List<Pair<String, Object>>>>> types;
60
61                 public TypedCode() {
62                         types = new ArrayList<Pair<String,Pair<HttpCode<TRANS,?>,List<Pair<String,Object>>>>>();
63                 }
64                 
65                 /**
66                  * Construct Typed Code based on ContentType parameters passed in
67                  * 
68                  * @param code
69                  * @param others
70                  * @return
71                  */
72                 public TypedCode<TRANS> add(HttpCode<TRANS,?> code, String ... others) {
73                         StringBuilder sb = new StringBuilder();
74                         boolean first = true;
75                         for(String str : others) {
76                                 if(first) {
77                                         first = false; 
78                                 } else {
79                                         sb.append(',');
80                                 }
81                                 sb.append(str);
82                         }
83                         parse(code, sb.toString());
84                         
85                         return this;
86                 }
87                 
88                 @Override
89                 protected Pair<String, Pair<HttpCode<TRANS,?>, List<Pair<String, Object>>>> types(HttpCode<TRANS,?> code, String str) {
90                         Pair<String, Pair<HttpCode<TRANS,?>,List<Pair<String, Object>>>> type = null;
91                         ArrayList<Pair<String, Object>> props = new ArrayList<Pair<String,Object>>();
92                         // Want Q percentage is to be first in the array everytime.  If not listed, 1.0 is default
93                         props.add(new Pair<String,Object>(Q,1f));
94                         Pair<HttpCode<TRANS,?>, List<Pair<String,Object>>> cl = new Pair<HttpCode<TRANS,?>, List<Pair<String,Object>>>(code, props);
95 //                      // breakup "plus" stuff, i.e. application/xaml+xml
96 //                      int plus = str.indexOf('+');
97 //                      if(plus<0) {
98                                 type = new Pair<String, Pair<HttpCode<TRANS,?>,List<Pair<String,Object>>>>(str, cl);
99                                 types.add(type);
100                                 return type;
101 //                      } else {
102 //                              int prev = str.indexOf('/')+1;
103 //                              String first = str.substring(0,prev);
104 //                              String nstr;
105 //                              while(prev!=0) {
106 //                                      nstr = first + (plus>-1?str.substring(prev,plus):str.substring(prev));
107 //                                      type = new Pair<String, Pair<HttpCode<TRANS,?>,List<Pair<String,Object>>>>(nstr, cl);
108 //                                      types.add(type);
109 //                                      prev = plus+1;
110 //                                      plus = str.indexOf('+',prev);
111 //                              }
112 //                      return type;
113 //                      }
114                 }
115
116                 @Override
117                 protected boolean props(Pair<String, Pair<HttpCode<TRANS,?>, List<Pair<String, Object>>>> type, String tag, String value) {
118                         if(tag.equals(Q)) { // reset the Q value (first in array)
119                                 boolean rv = true;
120                                 try {
121                                         type.y.y.get(0).y=Float.parseFloat(value);
122                                         return rv;
123                                 } catch (NumberFormatException e) {
124                                         rv=false; // Note: this awkward syntax forced by Sonar, which doesn't like doing nothing with Exception
125                                                           // which is what should happen
126                                 }
127                         }
128                         return type.y.y.add(new Pair<String,Object>(tag,"version".equals(tag)?new Version(value):value));
129                 }
130                 
131                 public Pair<String, Pair<HttpCode<TRANS, ?>, List<Pair<String, Object>>>> prep(TRANS trans, String compare) throws IOException, ServletException {
132                         Pair<String, Pair<HttpCode<TRANS,?>, List<Pair<String, Object>>>> c,rv=null;
133                         if(types.size()==1 && "".equals((c=types.get(0)).x)) { // if there are no checks for type, skip
134                                 rv = c;
135                         } else {
136                                 if(compare==null || compare.length()==0) {
137                                         rv = types.get(0); // first code is used
138                                 } else {
139                                         Acceptor<TRANS> acc = new Acceptor<TRANS>(types);
140                                         boolean accepted;
141                                         TimeTaken tt = trans.start(compare, Env.SUB);
142                                         try {
143                                                 accepted = acc.parse(null, compare);
144                                         } finally {
145                                                 tt.done();
146                                         }
147                                         if(accepted) {
148                                                 switch(acc.acceptable.size()) {
149                                                         case 0: 
150 //                                                              // TODO best Status Code?
151 //                                                              resp.setStatus(HttpStatus.NOT_ACCEPTABLE_406);
152                                                                 break;
153                                                         case 1: 
154                                                                 rv = acc.acceptable.get(0);
155                                                                 break;
156                                                         default: // compare Q values to get Best Match
157                                                                 float bestQ = -1.0f;
158                                                                 Pair<String, Pair<HttpCode<TRANS,?>, List<Pair<String, Object>>>> bestT = null;
159                                                                 for(Pair<String, Pair<HttpCode<TRANS,?>, List<Pair<String, Object>>>> type : acc.acceptable) {
160                                                                         Float f = (Float)type.y.y.get(0).y; // first property is always Q
161                                                                         if(f>bestQ) {
162                                                                                 bestQ=f;
163                                                                                 bestT = type;
164                                                                         }
165                                                                 }
166                                                                 if(bestT!=null) {
167                                                                         // When it is a GET, the matched type is what is returned, so set ContentType
168 //                                                                      if(isGet)resp.setContentType(bestT.x); // set ContentType of Code<TRANS,?>
169 //                                                                      rv = bestT.y.x;
170                                                                         rv = bestT;
171                                                                 }
172                                                 }
173                                         } else {
174                                                 trans.checkpoint("No Match found for Accept");
175                                         }
176                                 }
177                         }
178                         return rv;
179                 }
180                 
181                 /**
182                  * Print on String Builder content related to specific Code
183                  * 
184                  * This is for Reporting and Debugging purposes, so the content is not cached.
185                  * 
186                  * If code is "null", then all content is matched
187                  * 
188                  * @param code
189                  * @return
190                  */
191                 public StringBuilder relatedTo(HttpCode<TRANS, ?> code, StringBuilder sb) {
192                         boolean first = true;
193                         for(Pair<String, Pair<HttpCode<TRANS, ?>, List<Pair<String, Object>>>> pair : types) {
194                                 if(code==null || pair.y.x == code) {
195                                         if(first) {
196                                                 first = false;
197                                         } else {
198                                                 sb.append(',');
199                                         }
200                                         sb.append(pair.x);
201                                         for(Pair<String,Object> prop : pair.y.y) {
202                                                 // Don't print "Q".  it's there for internal use, but it is only meaningful for "Accepts"
203                                                 if(!prop.x.equals(Q) || !prop.y.equals(1f) ) {
204                                                         sb.append(';');
205                                                         sb.append(prop.x);
206                                                         sb.append('=');
207                                                         sb.append(prop.y);
208                                                 }
209                                         }
210                                 }
211                         }
212                         return sb;
213                 }
214                 
215                 public List<Pair<String, Object>> getContent(HttpCode<TRANS,?> code) {
216                         for(Pair<String, Pair<HttpCode<TRANS, ?>, List<Pair<String, Object>>>> pair : types) {
217                                 if(pair.y.x == code) {
218                                         return pair.y.y;
219                                 }
220                         }
221                         return null;
222                 }
223         
224                 public String toString() {
225                         return relatedTo(null,new StringBuilder()).toString();
226                 }
227                 
228                 public void api(RouteReport tr) {
229                         // Need to build up a map, because Prop entries can be in several places.
230                         HashMap<HttpCode<?,?>,StringBuilder> psb = new HashMap<HttpCode<?,?>,StringBuilder>();
231                         StringBuilder temp;
232                         tr.desc = null;
233                         
234                         // Read through Code/TypeCode trees for all accepted Typecodes
235                         for(Pair<String, Pair<HttpCode<TRANS, ?>, List<Pair<String, Object>>>> tc : types) {
236                                 // If new, then it's new Code set, create prefix content
237                                 if((temp=psb.get(tc.y.x))==null) {
238                                         psb.put(tc.y.x,temp=new StringBuilder());
239                                         if(tr.desc==null) {
240                                                 tr.desc = tc.y.x.desc();
241                                         }
242                                 } else {
243                                         temp.append(',');
244                                 }
245                                 temp.append(tc.x);
246
247                                 // add all properties
248                                 for(Pair<String, Object> props : tc.y.y) {
249                                         temp.append(';');
250                                         temp.append(props.x);
251                                         temp.append('=');
252                                         temp.append(props.y);
253                                 }
254                         }
255                         // Gather all ContentType possibilities for the same code together
256                         
257                         for(StringBuilder sb : psb.values()) {
258                                 tr.contextTypes.add(sb.toString());
259                         }
260                 }
261
262                 public String first() {
263                         if(types.size()>0) {
264                                 return types.get(0).x;
265                         }
266                         return null;
267                 }
268                 
269         }