+++ /dev/null
-/*******************************************************************************\r
- * ============LICENSE_START====================================================\r
- * * org.onap.aaf\r
- * * ===========================================================================\r
- * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.\r
- * * ===========================================================================\r
- * * Licensed under the Apache License, Version 2.0 (the "License");\r
- * * you may not use this file except in compliance with the License.\r
- * * You may obtain a copy of the License at\r
- * * \r
- * * http://www.apache.org/licenses/LICENSE-2.0\r
- * * \r
- * * Unless required by applicable law or agreed to in writing, software\r
- * * distributed under the License is distributed on an "AS IS" BASIS,\r
- * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * * See the License for the specific language governing permissions and\r
- * * limitations under the License.\r
- * * ============LICENSE_END====================================================\r
- * *\r
- * * ECOMP is a trademark and service mark of AT&T Intellectual Property.\r
- * *\r
- ******************************************************************************/\r
-package com.att.cssa.rserv;\r
-\r
-import java.io.IOException;\r
-import java.util.ArrayList;\r
-import java.util.HashMap;\r
-import java.util.List;\r
-\r
-import javax.servlet.ServletException;\r
-\r
-import com.att.inno.env.Env;\r
-import com.att.inno.env.TimeTaken;\r
-import com.att.inno.env.Trans;\r
-\r
-\r
-/**\r
- * TypedCode organizes implementation code based on the Type and Version of code it works with so that it can\r
- * be located quickly at runtime based on the "Accept" HTTP Header.\r
- *\r
- * FYI: For those in the future wondering why I would create a specialized set of "Pair" for the data content:\r
- * 1) TypeCode is used in Route, and this code is used for every transaction... it needs to be blazingly fast\r
- * 2) The actual number of objects accessed is quite small and built at startup. Arrays are best\r
- * 3) I needed a small, well defined tree where each level is a different Type. Using a "Pair" Generic definitions, \r
- * I created type-safety at each level, which you can't get from a TreeSet, etc.\r
- * 4) Chaining through the Network is simply object dereferencing, which is as fast as Java can go.\r
- * 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
- * read both in code, and in the debugger. However, TypeSafety allows your IDE (Eclipse) to help you make the \r
- * choices. Also, make sure you have a good "toString()" method on each object so you can see what's happening\r
- * in the IDE Debugger.\r
- * \r
- * Empirically, this method of obtaining routes proved to be much faster than the HashSet implementations available in otherwise\r
- * competent Open Source.\r
- *\r
- * @param <TRANS>\r
- */\r
-public class TypedCode<TRANS extends Trans> extends Content<TRANS> {\r
- private List<Pair<String, Pair<HttpCode<TRANS,?>,List<Pair<String, Object>>>>> types;\r
-\r
- public TypedCode() {\r
- types = new ArrayList<Pair<String,Pair<HttpCode<TRANS,?>,List<Pair<String,Object>>>>>();\r
- }\r
- \r
- /**\r
- * Construct Typed Code based on ContentType parameters passed in\r
- * \r
- * @param code\r
- * @param others\r
- * @return\r
- */\r
- public TypedCode<TRANS> add(HttpCode<TRANS,?> code, String ... others) {\r
- StringBuilder sb = new StringBuilder();\r
- boolean first = true;\r
- for(String str : others) {\r
- if(first) {\r
- first = false; \r
- } else {\r
- sb.append(',');\r
- }\r
- sb.append(str);\r
- }\r
- parse(code, sb.toString());\r
- \r
- return this;\r
- }\r
- \r
- @Override\r
- protected Pair<String, Pair<HttpCode<TRANS,?>, List<Pair<String, Object>>>> types(HttpCode<TRANS,?> code, String str) {\r
- Pair<String, Pair<HttpCode<TRANS,?>,List<Pair<String, Object>>>> type = null;\r
- ArrayList<Pair<String, Object>> props = new ArrayList<Pair<String,Object>>();\r
- // Want Q percentage is to be first in the array everytime. If not listed, 1.0 is default\r
- props.add(new Pair<String,Object>(Q,1f));\r
- Pair<HttpCode<TRANS,?>, List<Pair<String,Object>>> cl = new Pair<HttpCode<TRANS,?>, List<Pair<String,Object>>>(code, props);\r
-// // breakup "plus" stuff, i.e. application/xaml+xml\r
-// int plus = str.indexOf('+');\r
-// if(plus<0) {\r
- type = new Pair<String, Pair<HttpCode<TRANS,?>,List<Pair<String,Object>>>>(str, cl);\r
- types.add(type);\r
- return type;\r
-// } else {\r
-// int prev = str.indexOf('/')+1;\r
-// String first = str.substring(0,prev);\r
-// String nstr;\r
-// while(prev!=0) {\r
-// nstr = first + (plus>-1?str.substring(prev,plus):str.substring(prev));\r
-// type = new Pair<String, Pair<HttpCode<TRANS,?>,List<Pair<String,Object>>>>(nstr, cl);\r
-// types.add(type);\r
-// prev = plus+1;\r
-// plus = str.indexOf('+',prev);\r
-// }\r
-// return type;\r
-// }\r
- }\r
-\r
- @Override\r
- protected boolean props(Pair<String, Pair<HttpCode<TRANS,?>, List<Pair<String, Object>>>> type, String tag, String value) {\r
- if(tag.equals(Q)) { // reset the Q value (first in array)\r
- boolean rv = true;\r
- try {\r
- type.y.y.get(0).y=Float.parseFloat(value);\r
- return rv;\r
- } catch (NumberFormatException e) {\r
- rv=false; // Note: this awkward syntax forced by Sonar, which doesn't like doing nothing with Exception\r
- // which is what should happen\r
- }\r
- }\r
- return type.y.y.add(new Pair<String,Object>(tag,"version".equals(tag)?new Version(value):value));\r
- }\r
- \r
- public Pair<String, Pair<HttpCode<TRANS, ?>, List<Pair<String, Object>>>> prep(TRANS trans, String compare) throws IOException, ServletException {\r
- Pair<String, Pair<HttpCode<TRANS,?>, List<Pair<String, Object>>>> c,rv=null;\r
- if(types.size()==1 && "".equals((c=types.get(0)).x)) { // if there are no checks for type, skip\r
- rv = c;\r
- } else {\r
- if(compare==null || compare.length()==0) {\r
- rv = types.get(0); // first code is used\r
- } else {\r
- Acceptor<TRANS> acc = new Acceptor<TRANS>(types);\r
- boolean accepted;\r
- TimeTaken tt = trans.start(compare, Env.SUB);\r
- try {\r
- accepted = acc.parse(null, compare);\r
- } finally {\r
- tt.done();\r
- }\r
- if(accepted) {\r
- switch(acc.acceptable.size()) {\r
- case 0: \r
-// // TODO best Status Code?\r
-// resp.setStatus(HttpStatus.NOT_ACCEPTABLE_406);\r
- break;\r
- case 1: \r
- rv = acc.acceptable.get(0);\r
- break;\r
- default: // compare Q values to get Best Match\r
- float bestQ = -1.0f;\r
- Pair<String, Pair<HttpCode<TRANS,?>, List<Pair<String, Object>>>> bestT = null;\r
- for(Pair<String, Pair<HttpCode<TRANS,?>, List<Pair<String, Object>>>> type : acc.acceptable) {\r
- Float f = (Float)type.y.y.get(0).y; // first property is always Q\r
- if(f>bestQ) {\r
- bestQ=f;\r
- bestT = type;\r
- }\r
- }\r
- if(bestT!=null) {\r
- // When it is a GET, the matched type is what is returned, so set ContentType\r
-// if(isGet)resp.setContentType(bestT.x); // set ContentType of Code<TRANS,?>\r
-// rv = bestT.y.x;\r
- rv = bestT;\r
- }\r
- }\r
- } else {\r
- trans.checkpoint("No Match found for Accept");\r
- }\r
- }\r
- }\r
- return rv;\r
- }\r
- \r
- /**\r
- * Print on String Builder content related to specific Code\r
- * \r
- * This is for Reporting and Debugging purposes, so the content is not cached.\r
- * \r
- * If code is "null", then all content is matched\r
- * \r
- * @param code\r
- * @return\r
- */\r
- public StringBuilder relatedTo(HttpCode<TRANS, ?> code, StringBuilder sb) {\r
- boolean first = true;\r
- for(Pair<String, Pair<HttpCode<TRANS, ?>, List<Pair<String, Object>>>> pair : types) {\r
- if(code==null || pair.y.x == code) {\r
- if(first) {\r
- first = false;\r
- } else {\r
- sb.append(',');\r
- }\r
- sb.append(pair.x);\r
- for(Pair<String,Object> prop : pair.y.y) {\r
- // Don't print "Q". it's there for internal use, but it is only meaningful for "Accepts"\r
- if(!prop.x.equals(Q) || !prop.y.equals(1f) ) {\r
- sb.append(';');\r
- sb.append(prop.x);\r
- sb.append('=');\r
- sb.append(prop.y);\r
- }\r
- }\r
- }\r
- }\r
- return sb;\r
- }\r
- \r
- public List<Pair<String, Object>> getContent(HttpCode<TRANS,?> code) {\r
- for(Pair<String, Pair<HttpCode<TRANS, ?>, List<Pair<String, Object>>>> pair : types) {\r
- if(pair.y.x == code) {\r
- return pair.y.y;\r
- }\r
- }\r
- return null;\r
- }\r
- \r
- public String toString() {\r
- return relatedTo(null,new StringBuilder()).toString();\r
- }\r
- \r
- public void api(RouteReport tr) {\r
- // Need to build up a map, because Prop entries can be in several places.\r
- HashMap<HttpCode<?,?>,StringBuilder> psb = new HashMap<HttpCode<?,?>,StringBuilder>();\r
- StringBuilder temp;\r
- tr.desc = null;\r
- \r
- // Read through Code/TypeCode trees for all accepted Typecodes\r
- for(Pair<String, Pair<HttpCode<TRANS, ?>, List<Pair<String, Object>>>> tc : types) {\r
- // If new, then it's new Code set, create prefix content\r
- if((temp=psb.get(tc.y.x))==null) {\r
- psb.put(tc.y.x,temp=new StringBuilder());\r
- if(tr.desc==null) {\r
- tr.desc = tc.y.x.desc();\r
- }\r
- } else {\r
- temp.append(',');\r
- }\r
- temp.append(tc.x);\r
-\r
- // add all properties\r
- for(Pair<String, Object> props : tc.y.y) {\r
- temp.append(';');\r
- temp.append(props.x);\r
- temp.append('=');\r
- temp.append(props.y);\r
- }\r
- }\r
- // Gather all ContentType possibilities for the same code together\r
- \r
- for(StringBuilder sb : psb.values()) {\r
- tr.contextTypes.add(sb.toString());\r
- }\r
- }\r
-\r
- public String first() {\r
- if(types.size()>0) {\r
- return types.get(0).x;\r
- }\r
- return null;\r
- }\r
- \r
- }\r