1 /*******************************************************************************
\r
2 * ============LICENSE_START====================================================
\r
4 * * ===========================================================================
\r
5 * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
\r
6 * * Copyright © 2017 Amdocs
\r
7 * * ===========================================================================
\r
8 * * Licensed under the Apache License, Version 2.0 (the "License");
\r
9 * * you may not use this file except in compliance with the License.
\r
10 * * You may obtain a copy of the License at
\r
12 * * http://www.apache.org/licenses/LICENSE-2.0
\r
14 * * Unless required by applicable law or agreed to in writing, software
\r
15 * * distributed under the License is distributed on an "AS IS" BASIS,
\r
16 * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
\r
17 * * See the License for the specific language governing permissions and
\r
18 * * limitations under the License.
\r
19 * * ============LICENSE_END====================================================
\r
21 * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
\r
23 ******************************************************************************/
\r
24 package com.att.cssa.rserv;
\r
26 import java.io.IOException;
\r
27 import java.util.ArrayList;
\r
28 import java.util.HashMap;
\r
29 import java.util.List;
\r
31 import javax.servlet.ServletException;
\r
33 import com.att.inno.env.Env;
\r
34 import com.att.inno.env.TimeTaken;
\r
35 import com.att.inno.env.Trans;
\r
39 * TypedCode organizes implementation code based on the Type and Version of code it works with so that it can
\r
40 * be located quickly at runtime based on the "Accept" HTTP Header.
\r
42 * FYI: For those in the future wondering why I would create a specialized set of "Pair" for the data content:
\r
43 * 1) TypeCode is used in Route, and this code is used for every transaction... it needs to be blazingly fast
\r
44 * 2) The actual number of objects accessed is quite small and built at startup. Arrays are best
\r
45 * 3) I needed a small, well defined tree where each level is a different Type. Using a "Pair" Generic definitions,
\r
46 * I created type-safety at each level, which you can't get from a TreeSet, etc.
\r
47 * 4) Chaining through the Network is simply object dereferencing, which is as fast as Java can go.
\r
48 * 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
\r
49 * read both in code, and in the debugger. However, TypeSafety allows your IDE (Eclipse) to help you make the
\r
50 * choices. Also, make sure you have a good "toString()" method on each object so you can see what's happening
\r
51 * in the IDE Debugger.
\r
53 * Empirically, this method of obtaining routes proved to be much faster than the HashSet implementations available in otherwise
\r
54 * competent Open Source.
\r
58 public class TypedCode<TRANS extends Trans> extends Content<TRANS> {
\r
59 private List<Pair<String, Pair<HttpCode<TRANS,?>,List<Pair<String, Object>>>>> types;
\r
61 public TypedCode() {
\r
62 types = new ArrayList<Pair<String,Pair<HttpCode<TRANS,?>,List<Pair<String,Object>>>>>();
\r
66 * Construct Typed Code based on ContentType parameters passed in
\r
72 public TypedCode<TRANS> add(HttpCode<TRANS,?> code, String ... others) {
\r
73 StringBuilder sb = new StringBuilder();
\r
74 boolean first = true;
\r
75 for(String str : others) {
\r
83 parse(code, sb.toString());
\r
89 protected Pair<String, Pair<HttpCode<TRANS,?>, List<Pair<String, Object>>>> types(HttpCode<TRANS,?> code, String str) {
\r
90 Pair<String, Pair<HttpCode<TRANS,?>,List<Pair<String, Object>>>> type = null;
\r
91 ArrayList<Pair<String, Object>> props = new ArrayList<Pair<String,Object>>();
\r
92 // Want Q percentage is to be first in the array everytime. If not listed, 1.0 is default
\r
93 props.add(new Pair<String,Object>(Q,1f));
\r
94 Pair<HttpCode<TRANS,?>, List<Pair<String,Object>>> cl = new Pair<HttpCode<TRANS,?>, List<Pair<String,Object>>>(code, props);
\r
95 // // breakup "plus" stuff, i.e. application/xaml+xml
\r
96 // int plus = str.indexOf('+');
\r
98 type = new Pair<String, Pair<HttpCode<TRANS,?>,List<Pair<String,Object>>>>(str, cl);
\r
102 // int prev = str.indexOf('/')+1;
\r
103 // String first = str.substring(0,prev);
\r
105 // while(prev!=0) {
\r
106 // nstr = first + (plus>-1?str.substring(prev,plus):str.substring(prev));
\r
107 // type = new Pair<String, Pair<HttpCode<TRANS,?>,List<Pair<String,Object>>>>(nstr, cl);
\r
108 // types.add(type);
\r
110 // plus = str.indexOf('+',prev);
\r
117 protected boolean props(Pair<String, Pair<HttpCode<TRANS,?>, List<Pair<String, Object>>>> type, String tag, String value) {
\r
118 if(tag.equals(Q)) { // reset the Q value (first in array)
\r
121 type.y.y.get(0).y=Float.parseFloat(value);
\r
123 } catch (NumberFormatException e) {
\r
124 rv=false; // Note: this awkward syntax forced by Sonar, which doesn't like doing nothing with Exception
\r
125 // which is what should happen
\r
128 return type.y.y.add(new Pair<String,Object>(tag,"version".equals(tag)?new Version(value):value));
\r
131 public Pair<String, Pair<HttpCode<TRANS, ?>, List<Pair<String, Object>>>> prep(TRANS trans, String compare) throws IOException, ServletException {
\r
132 Pair<String, Pair<HttpCode<TRANS,?>, List<Pair<String, Object>>>> c,rv=null;
\r
133 if(types.size()==1 && "".equals((c=types.get(0)).x)) { // if there are no checks for type, skip
\r
136 if(compare==null || compare.length()==0) {
\r
137 rv = types.get(0); // first code is used
\r
139 Acceptor<TRANS> acc = new Acceptor<TRANS>(types);
\r
141 TimeTaken tt = trans.start(compare, Env.SUB);
\r
143 accepted = acc.parse(null, compare);
\r
148 switch(acc.acceptable.size()) {
\r
150 // // TODO best Status Code?
\r
151 // resp.setStatus(HttpStatus.NOT_ACCEPTABLE_406);
\r
154 rv = acc.acceptable.get(0);
\r
156 default: // compare Q values to get Best Match
\r
157 float bestQ = -1.0f;
\r
158 Pair<String, Pair<HttpCode<TRANS,?>, List<Pair<String, Object>>>> bestT = null;
\r
159 for(Pair<String, Pair<HttpCode<TRANS,?>, List<Pair<String, Object>>>> type : acc.acceptable) {
\r
160 Float f = (Float)type.y.y.get(0).y; // first property is always Q
\r
167 // When it is a GET, the matched type is what is returned, so set ContentType
\r
168 // if(isGet)resp.setContentType(bestT.x); // set ContentType of Code<TRANS,?>
\r
174 trans.checkpoint("No Match found for Accept");
\r
182 * Print on String Builder content related to specific Code
\r
184 * This is for Reporting and Debugging purposes, so the content is not cached.
\r
186 * If code is "null", then all content is matched
\r
191 public StringBuilder relatedTo(HttpCode<TRANS, ?> code, StringBuilder sb) {
\r
192 boolean first = true;
\r
193 for(Pair<String, Pair<HttpCode<TRANS, ?>, List<Pair<String, Object>>>> pair : types) {
\r
194 if(code==null || pair.y.x == code) {
\r
201 for(Pair<String,Object> prop : pair.y.y) {
\r
202 // Don't print "Q". it's there for internal use, but it is only meaningful for "Accepts"
\r
203 if(!prop.x.equals(Q) || !prop.y.equals(1f) ) {
\r
215 public List<Pair<String, Object>> getContent(HttpCode<TRANS,?> code) {
\r
216 for(Pair<String, Pair<HttpCode<TRANS, ?>, List<Pair<String, Object>>>> pair : types) {
\r
217 if(pair.y.x == code) {
\r
224 public String toString() {
\r
225 return relatedTo(null,new StringBuilder()).toString();
\r
228 public void api(RouteReport tr) {
\r
229 // Need to build up a map, because Prop entries can be in several places.
\r
230 HashMap<HttpCode<?,?>,StringBuilder> psb = new HashMap<HttpCode<?,?>,StringBuilder>();
\r
231 StringBuilder temp;
\r
234 // Read through Code/TypeCode trees for all accepted Typecodes
\r
235 for(Pair<String, Pair<HttpCode<TRANS, ?>, List<Pair<String, Object>>>> tc : types) {
\r
236 // If new, then it's new Code set, create prefix content
\r
237 if((temp=psb.get(tc.y.x))==null) {
\r
238 psb.put(tc.y.x,temp=new StringBuilder());
\r
239 if(tr.desc==null) {
\r
240 tr.desc = tc.y.x.desc();
\r
247 // add all properties
\r
248 for(Pair<String, Object> props : tc.y.y) {
\r
250 temp.append(props.x);
\r
252 temp.append(props.y);
\r
255 // Gather all ContentType possibilities for the same code together
\r
257 for(StringBuilder sb : psb.values()) {
\r
258 tr.contextTypes.add(sb.toString());
\r
262 public String first() {
\r
263 if(types.size()>0) {
\r
264 return types.get(0).x;
\r