2 * ============LICENSE_START====================================================
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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====================================================
22 package org.onap.aaf.auth.rserv;
24 import java.util.HashMap;
29 * This path matching algorithm avoids using split strings during the critical transactional run-time. By pre-analyzing the
30 * content at "set Param" time, and storing data in an array-index model which presumably is done once and at the beginning,
31 * we can match in much less time when it actually counts.
37 private Map<String, Integer> params;
38 private byte[][] values;
39 private Integer[] vars;
40 private boolean wildcard;
44 * These two methods are pairs of searching performance for variables Spark Style.
45 * setParams evaluates the target path, and sets a HashMap that will return an Integer.
46 * the Keys are both :key and key so that there will be no string operations during
49 * For the Integer, if the High Order is 0, then it is just one value. If High Order >0, then it is
50 * a multi-field option, i.e. ending with a wild-card.
52 public Match(String path) {
53 params = new HashMap<>();
55 String[] pa = path.split("/");
56 values = new byte[pa.length][];
57 vars = new Integer[pa.length];
61 for (int i=0;i<pa.length && !wildcard;++i) {
62 if (pa[i].startsWith(":")) {
63 if (pa[i].endsWith("*")) {
64 val = i | pa.length<<16; // load end value in high order bits
65 key = pa[i].substring(0, pa[i].length()-1);// remove *
71 params.put(key,val); //put in :key
72 params.put(key.substring(1,key.length()), val); // put in just key, better than adding a missing one, like Spark
73 // values[i]=null; // null stands for Variable
76 values[i]=pa[i].getBytes();
77 if (pa[i].endsWith("*")) {
79 if (pa[i].length()>1) {
80 /* remove * from value */
81 int newlength = values[i].length-1;
82 byte[] real = new byte[newlength];
83 System.arraycopy(values[i],0,real,0,newlength);
86 vars[i]=0; // this is actually a variable, if it only contains a "*"
95 * This is the second of the param evaluation functions. First, we look up to see if there is
96 * any reference by key in the params Map created by the above.
98 * The resulting Integer, if not null, is split high/low order into start and end.
99 * We evaluate the string for '/', rather than splitting into String[] to avoid the time/mem needed
100 * We traverse to the proper field number for slash, evaluate the end (whether wild card or no),
101 * and return the substring.
103 * The result is something less than .003 milliseconds per evaluation
106 public String param(String path,String key) {
107 Integer val = params.get(key); // :key or key
109 int start = val & 0xFFFF;
110 int end = (val >> 16) & 0xFFFF;
113 for (i=0;i<start;++i) {
114 idx = path.indexOf('/',idx+1);
120 end = path.indexOf('/',idx);
121 if (end<0)end=path.length();
125 return path.substring(idx,end);
126 } else if (i==start-1) { // if last spot was left blank, i.e. :key*
133 public boolean match(String path) {
134 if (path==null|| path.length()==0 || "/".equals(path) ) {
135 if (values==null)return true;
136 switch(values.length) {
138 case 1: return values[0].length==0;
139 default: return false;
143 byte[] pabytes = path.getBytes();
147 int lastField = values.length;
148 int lastByte = pabytes.length;
149 boolean fieldMatched = false;
150 for (int i=0;rv && i<lastByte;++i) {
151 if (field>=lastField) { // checking here allows there to be a non-functional ending /
155 if (values[field]==null) { // it's a variable, just look for /s
156 if (wildcard && field==lastField-1) return true;// we've made it this far. We accept all remaining characters
157 Integer val = vars[field];
158 int start = val & 0xFFFF;
159 int end = (val >> 16) & 0xFFFF;
160 if (end==0)end=start+1;
162 for (int j=start; j<end && k<lastByte; ++k) {
163 if (pabytes[k]=='/') {
169 if (k==lastByte && pabytes[k-1]!='/')++field;
170 if (k>i)i=k-1; // if we've incremented, have to accommodate the outer for loop incrementing as well
171 fieldMatched = false; // reset
174 if (pabytes[i]=='/') { // end of field, eval if Field is matched
175 // if double slash, check if supposed to be empty
176 if (fieldIdx==0 && values[field].length==0) {
179 rv = fieldMatched && ++field<lastField;
181 fieldMatched = false;
183 } else if (values[field].length==0) {
184 // double slash in path, but content in field. We check specially here to avoid
185 // Array out of bounds issues.
189 rv =false; // field is already matched, now there's too many bytes
191 rv = pabytes[i]==values[field][fieldIdx++]; // compare expected (pabytes[i]) with value for particular field
192 fieldMatched=values[field].length==fieldIdx; // are all the bytes match in the field?
193 if (fieldMatched && (i==lastByte-1 || (wildcard && field==lastField-1)))
194 return true; // last field info
199 if (field!=lastField || pabytes.length!=lastByte) rv = false; // have we matched all the fields and all the bytes?
203 public Set<String> getParamNames() {
204 return params.keySet();