summaryrefslogtreecommitdiffstats
path: root/authz-core/src/main/java/com/att/cssa
diff options
context:
space:
mode:
authorsg481n <sg481n@att.com>2017-08-03 17:27:34 -0400
committersg481n <sg481n@att.com>2017-08-03 17:27:34 -0400
commit43854a9e3310ff7a92257d16c4fc0a8321eaec68 (patch)
tree46af936c5da4f9c60d7d63dade5c61a8fd5ef9f4 /authz-core/src/main/java/com/att/cssa
parentf691a8b8dfc9eea4c6b3bfa45ea60f07ad347e69 (diff)
 [AAF-21] Initial code import
Change-Id: I63d7d499bbd46f500b5f5a4db966166f613f327a Signed-off-by: sg481n <sg481n@att.com>
Diffstat (limited to 'authz-core/src/main/java/com/att/cssa')
-rw-r--r--authz-core/src/main/java/com/att/cssa/rserv/Acceptor.java170
-rw-r--r--authz-core/src/main/java/com/att/cssa/rserv/CachingFileAccess.java477
-rw-r--r--authz-core/src/main/java/com/att/cssa/rserv/CodeSetter.java54
-rw-r--r--authz-core/src/main/java/com/att/cssa/rserv/Content.java116
-rw-r--r--authz-core/src/main/java/com/att/cssa/rserv/HttpCode.java112
-rw-r--r--authz-core/src/main/java/com/att/cssa/rserv/HttpMethods.java31
-rw-r--r--authz-core/src/main/java/com/att/cssa/rserv/Match.java212
-rw-r--r--authz-core/src/main/java/com/att/cssa/rserv/Pair.java44
-rw-r--r--authz-core/src/main/java/com/att/cssa/rserv/RServlet.java156
-rw-r--r--authz-core/src/main/java/com/att/cssa/rserv/Route.java143
-rw-r--r--authz-core/src/main/java/com/att/cssa/rserv/RouteReport.java35
-rw-r--r--authz-core/src/main/java/com/att/cssa/rserv/Routes.java91
-rw-r--r--authz-core/src/main/java/com/att/cssa/rserv/TransFilter.java137
-rw-r--r--authz-core/src/main/java/com/att/cssa/rserv/TransOnlyFilter.java78
-rw-r--r--authz-core/src/main/java/com/att/cssa/rserv/TypedCode.java269
-rw-r--r--authz-core/src/main/java/com/att/cssa/rserv/Version.java94
-rw-r--r--authz-core/src/main/java/com/att/cssa/rserv/doc/ApiDoc.java43
17 files changed, 2262 insertions, 0 deletions
diff --git a/authz-core/src/main/java/com/att/cssa/rserv/Acceptor.java b/authz-core/src/main/java/com/att/cssa/rserv/Acceptor.java
new file mode 100644
index 00000000..ea2850aa
--- /dev/null
+++ b/authz-core/src/main/java/com/att/cssa/rserv/Acceptor.java
@@ -0,0 +1,170 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.aai
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * Copyright © 2017 Amdocs
+ * * ===========================================================================
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ * * http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ * * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+package com.att.cssa.rserv;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import com.att.inno.env.Trans;
+
+/**
+ * Find Acceptable Paths and place them where TypeCode can evaluate.
+ *
+ * If there are more than one, TypeCode will choose based on "q" value
+ *
+ * @param <TRANS>
+ */
+class Acceptor<TRANS extends Trans> {
+ private List<Pair<String, Pair<HttpCode<TRANS,?>, List<Pair<String, Object>>>>> types;
+ List<Pair<String, Pair<HttpCode<TRANS,?>, List<Pair<String, Object>>>>> acceptable;
+
+ public Acceptor(List<Pair<String, Pair<HttpCode<TRANS,?>, List<Pair<String, Object>>>>> types) {
+ this.types = types;
+ acceptable = new ArrayList<Pair<String, Pair<HttpCode<TRANS,?>, List<Pair<String, Object>>>>>();
+ }
+
+ private boolean eval(HttpCode<TRANS,?> code, String str, List<String> props) {
+// int plus = str.indexOf('+');
+// if(plus<0) {
+ boolean ok = false;
+ boolean any = false;
+ for(Pair<String, Pair<HttpCode<TRANS,?>, List<Pair<String, Object>>>> type : types) {
+ ok = true;
+ if(type.x.equals(str)) {
+ for(Iterator<String> iter = props.iterator();ok && iter.hasNext();) {
+ ok = props(type,iter.next(),iter.next());
+ }
+ if(ok) {
+ any = true;
+ acceptable.add(type);
+ }
+ }
+ }
+// } else { // Handle Accepts with "+" as in application/xaml+xml
+// int prev = str.indexOf('/')+1;
+// String first = str.substring(0,prev);
+// String nstr;
+// while(prev!=0) {
+// nstr = first + (plus<0?str.substring(prev):str.substring(prev,plus));
+//
+// for(Pair<String, Pair<HttpCode<TRANS,?>, List<Pair<String, Object>>>> type : types) {
+// if(type.x.equals(nstr)) {
+// acceptable.add(type);
+// return type;
+// }
+// }
+// prev = plus+1;
+// plus=str.indexOf('+', prev);
+// };
+// }
+ return any;
+ }
+
+ /**
+ * Evaluate Properties
+ * @param type
+ * @param tag
+ * @param value
+ * @return
+ */
+ private boolean props(Pair<String, Pair<HttpCode<TRANS,?>, List<Pair<String, Object>>>> type, String tag, String value) {
+ boolean rv = false;
+ if(type.y!=null) {
+ for(Pair<String,Object> prop : type.y.y){
+ if(tag.equals(prop.x)) {
+ if(tag.equals("charset")) {
+ return prop.x==null?false:prop.y.equals(value.toLowerCase()); // return True if Matched
+ } else if(tag.equals("version")) {
+ return prop.y.equals(new Version(value)); // Note: Version Class knows Minor Version encoding
+ } else if(tag.equals(Content.Q)) { // replace Q value
+ try {
+ type.y.y.get(0).y=Float.parseFloat(value);
+ } catch (NumberFormatException e) {
+ rv=false; // need to do something to make Sonar happy. But nothing to do.
+ }
+ return true;
+ } else {
+ return value.equals(prop.y);
+ }
+ }
+ }
+ }
+ return rv;
+ }
+
+ /**
+ * parse
+ *
+ * Note: I'm processing by index to avoid lots of memory creation, which speeds things
+ * up for this time critical section of code.
+ * @param code
+ * @param cntnt
+ * @return
+ */
+ protected boolean parse(HttpCode<TRANS, ?> code, String cntnt) {
+ byte bytes[] = cntnt.getBytes();
+
+ int cis,cie=-1,cend;
+ int sis,sie,send;
+ String name;
+ ArrayList<String> props = new ArrayList<String>();
+ do {
+ // Clear these in case more than one Semi
+ props.clear(); // on loop, do not want mixed properties
+ name=null;
+
+ cis = cie+1; // find comma start
+ while(cis<bytes.length && Character.isSpaceChar(bytes[cis]))++cis;
+ cie = cntnt.indexOf(',',cis); // find comma end
+ cend = cie<0?bytes.length:cie; // If no comma, set comma end to full length, else cie
+ while(cend>cis && Character.isSpaceChar(bytes[cend-1]))--cend;
+ // Start SEMIS
+ sie=cis-1;
+ do {
+ sis = sie+1; // semi start is one after previous end
+ while(sis<bytes.length && Character.isSpaceChar(bytes[sis]))++sis;
+ sie = cntnt.indexOf(';',sis);
+ send = sie>cend || sie<0?cend:sie; // if the Semicolon is after the comma, or non-existent, use comma end, else keep
+ while(send>sis && Character.isSpaceChar(bytes[send-1]))--send;
+ if(name==null) { // first entry in Comma set is the name, not a property
+ name = new String(bytes,sis,send-sis);
+ } else { // We've looped past the first Semi, now process as properties
+ // If there are additional elements (more entities within Semi Colons)
+ // apply Properties
+ int eq = cntnt.indexOf('=',sis);
+ if(eq>sis && eq<send) {
+ props.add(new String(bytes,sis,eq-sis));
+ props.add(new String(bytes,eq+1,send-(eq+1)));
+ }
+ }
+ // End Property
+ } while(sie<=cend && sie>=cis); // End SEMI processing
+ // Now evaluate Comma set and return if true
+ if(eval(code,name,props))return true; // else loop again to check next comma
+ } while(cie>=0); // loop to next comma
+ return false; // didn't get even one match
+ }
+
+}
diff --git a/authz-core/src/main/java/com/att/cssa/rserv/CachingFileAccess.java b/authz-core/src/main/java/com/att/cssa/rserv/CachingFileAccess.java
new file mode 100644
index 00000000..80de2692
--- /dev/null
+++ b/authz-core/src/main/java/com/att/cssa/rserv/CachingFileAccess.java
@@ -0,0 +1,477 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.aai
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * Copyright © 2017 Amdocs
+ * * ===========================================================================
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ * * http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ * * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+package com.att.cssa.rserv;
+
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.Writer;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.NavigableMap;
+import java.util.Set;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.TreeMap;
+import java.util.concurrent.ConcurrentSkipListMap;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import com.att.aft.dme2.internal.jetty.http.HttpStatus;
+import com.att.inno.env.Env;
+import com.att.inno.env.EnvJAXB;
+import com.att.inno.env.LogTarget;
+import com.att.inno.env.Store;
+import com.att.inno.env.TimeTaken;
+import com.att.inno.env.Trans;
+/*
+ * CachingFileAccess
+ *
+ *
+ */
+public class CachingFileAccess<TRANS extends Trans> extends HttpCode<TRANS, Void> {
+ public static void setEnv(Store store, String[] args) {
+ for(int i=0;i<args.length-1;i+=2) { // cover two parms required for each
+ if(CFA_WEB_DIR.equals(args[i])) {
+ store.put(store.staticSlot(CFA_WEB_DIR), args[i+1]);
+ } else if(CFA_CACHE_CHECK_INTERVAL.equals(args[i])) {
+ store.put(store.staticSlot(CFA_CACHE_CHECK_INTERVAL), Long.parseLong(args[i+1]));
+ } else if(CFA_MAX_SIZE.equals(args[i])) {
+ store.put(store.staticSlot(CFA_MAX_SIZE), Integer.parseInt(args[i+1]));
+ }
+ }
+ }
+
+ private static String MAX_AGE = "max-age=3600"; // 1 hour Caching
+ private final Map<String,String> typeMap;
+ private final NavigableMap<String,Content> content;
+ private final Set<String> attachOnly;
+ private final static String WEB_DIR_DEFAULT = "theme";
+ public final static String CFA_WEB_DIR = "CFA_WebPath";
+ // when to re-validate from file
+ // Re validating means comparing the Timestamp on the disk, and seeing it has changed. Cache is not marked
+ // dirty unless file has changed, but it still makes File IO, which for some kinds of cached data, i.e.
+ // deployed GUI elements is unnecessary, and wastes time.
+ // This parameter exists to cover the cases where data can be more volatile, so the user can choose how often the
+ // File IO will be accessed, based on probability of change. "0", of course, means, check every time.
+ private final static String CFA_CACHE_CHECK_INTERVAL = "CFA_CheckIntervalMS";
+ private final static String CFA_MAX_SIZE = "CFA_MaxSize"; // Cache size limit
+ private final static String CFA_CLEAR_COMMAND = "CFA_ClearCommand";
+
+ // Note: can be null without a problem, but included
+ // to tie in with existing Logging.
+ public LogTarget logT = null;
+ public long checkInterval; // = 600000L; // only check if not hit in 10 mins by default
+ public int maxItemSize; // = 512000; // max file 500k
+ private Timer timer;
+ private String web_path;
+ // A command key is set in the Properties, preferably changed on deployment.
+ // it is compared at the beginning of the path, and if so, it is assumed to issue certain commands
+ // It's purpose is to protect, to some degree the command, even though it is HTTP, allowing
+ // local batch files to, for instance, clear caches on resetting of files.
+ private String clear_command;
+
+ public CachingFileAccess(EnvJAXB env, String ... args) {
+ super(null,"Caching File Access");
+ setEnv(env,args);
+ content = new ConcurrentSkipListMap<String,Content>(); // multi-thread changes possible
+
+ attachOnly = new HashSet<String>(); // short, unchanged
+
+ typeMap = new TreeMap<String,String>(); // Structure unchanged after Construction
+ typeMap.put("ico","image/icon");
+ typeMap.put("html","text/html");
+ typeMap.put("css","text/css");
+ typeMap.put("js","text/javascript");
+ typeMap.put("txt","text/plain");
+ typeMap.put("xml","text/xml");
+ typeMap.put("xsd","text/xml");
+ attachOnly.add("xsd");
+ typeMap.put("crl", "application/x-pkcs7-crl");
+ typeMap.put("appcache","text/cache-manifest");
+
+ typeMap.put("json","text/json");
+ typeMap.put("ogg", "audio/ogg");
+ typeMap.put("jpg","image/jpeg");
+ typeMap.put("gif","image/gif");
+ typeMap.put("png","image/png");
+ typeMap.put("svg","image/svg+xml");
+ typeMap.put("jar","application/x-java-applet");
+ typeMap.put("jnlp", "application/x-java-jnlp-file");
+ typeMap.put("class", "application/java");
+
+ timer = new Timer("Caching Cleanup",true);
+ timer.schedule(new Cleanup(content,500),60000,60000);
+
+ // Property params
+ web_path = env.getProperty(CFA_WEB_DIR,WEB_DIR_DEFAULT);
+ Object obj;
+ obj = env.get(env.staticSlot(CFA_CACHE_CHECK_INTERVAL),600000L); // Default is 10 mins
+ if(obj instanceof Long) {checkInterval=(Long)obj;
+ } else {checkInterval=Long.parseLong((String)obj);}
+
+ obj = env.get(env.staticSlot(CFA_MAX_SIZE), 512000); // Default is max file 500k
+ if(obj instanceof Integer) {maxItemSize=(Integer)obj;
+ } else {maxItemSize =Integer.parseInt((String)obj);}
+
+ clear_command = env.getProperty(CFA_CLEAR_COMMAND,null);
+ }
+
+
+
+ @Override
+ public void handle(TRANS trans, HttpServletRequest req, HttpServletResponse resp) throws IOException {
+ String key = pathParam(req, ":key");
+ if(key.equals(clear_command)) {
+ String cmd = pathParam(req,":cmd");
+ resp.setHeader("Content-type",typeMap.get("txt"));
+ if("clear".equals(cmd)) {
+ content.clear();
+ resp.setStatus(HttpStatus.OK_200);
+ } else {
+ resp.setStatus(HttpStatus.BAD_REQUEST_400);
+ }
+ return;
+ }
+ Content c = load(logT , web_path,key, null, checkInterval);
+ if(c.attachmentOnly) {
+ resp.setHeader("Content-disposition", "attachment");
+ }
+ c.write(resp.getOutputStream());
+ c.setHeader(resp);
+ trans.checkpoint(req.getPathInfo());
+ }
+
+
+ public String webPath() {
+ return web_path;
+ }
+
+ /**
+ * Reset the Cleanup size and interval
+ *
+ * The size and interval when started are 500 items (memory size unknown) checked every minute in a background thread.
+ *
+ * @param size
+ * @param interval
+ */
+ public void cleanupParams(int size, long interval) {
+ timer.cancel();
+ timer.schedule(new Cleanup(content,size), interval, interval);
+ }
+
+
+
+ /**
+ * Load a file, first checking cache
+ *
+ *
+ * @param logTarget - logTarget can be null (won't log)
+ * @param dataRoot - data root storage directory
+ * @param key - relative File Path
+ * @param mediaType - what kind of file is it. If null, will check via file extension
+ * @param timeCheck - "-1" will take system default - Otherwise, will compare "now" + timeCheck(Millis) before looking at File mod
+ * @return
+ * @throws IOException
+ */
+ public Content load(LogTarget logTarget, String dataRoot, String key, String mediaType, long _timeCheck) throws IOException {
+ long timeCheck = _timeCheck;
+ if(timeCheck<0) {
+ timeCheck=checkInterval; // if time < 0, then use default
+ }
+ String fileName = dataRoot + '/' + key;
+ Content c = content.get(key);
+ long systime = System.currentTimeMillis();
+ File f=null;
+ if(c!=null) {
+ // Don't check every hit... only after certain time value
+ if(c.date < systime + timeCheck) {
+ f = new File(fileName);
+ if(f.lastModified()>c.date) {
+ c=null;
+ }
+ }
+ }
+ if(c==null) {
+ if(logTarget!=null) {
+ logTarget.log("File Read: ",key);
+ }
+
+ if(f==null){
+ f = new File(fileName);
+ }
+
+ boolean cacheMe;
+ if(f.exists()) {
+ if(f.length() > maxItemSize) {
+ c = new DirectFileContent(f);
+ cacheMe = false;
+ } else {
+ c = new CachedContent(f);
+ cacheMe = checkInterval>0;
+ }
+
+ if(mediaType==null) { // determine from file Ending
+ int idx = key.lastIndexOf('.');
+ String subkey = key.substring(++idx);
+ if((c.contentType = idx<0?null:typeMap.get(subkey))==null) {
+ // if nothing else, just set to default type...
+ c.contentType = "application/octet-stream";
+ }
+ c.attachmentOnly = attachOnly.contains(subkey);
+ } else {
+ c.contentType=mediaType;
+ c.attachmentOnly = false;
+ }
+
+ c.date = f.lastModified();
+
+ if(cacheMe) {
+ content.put(key, c);
+ }
+ } else {
+ c=NULL;
+ }
+ } else {
+ if(logTarget!=null)logTarget.log("Cache Read: ",key);
+ }
+
+ // refresh hit time
+ c.access = systime;
+ return c;
+ }
+
+ public Content loadOrDefault(Trans trans, String targetDir, String targetFileName, String sourcePath, String mediaType) throws IOException {
+ try {
+ return load(trans.info(),targetDir,targetFileName,mediaType,0);
+ } catch(FileNotFoundException e) {
+ String targetPath = targetDir + '/' + targetFileName;
+ TimeTaken tt = trans.start("File doesn't exist; copy " + sourcePath + " to " + targetPath, Env.SUB);
+ try {
+ FileInputStream sourceFIS = new FileInputStream(sourcePath);
+ FileChannel sourceFC = sourceFIS.getChannel();
+ File targetFile = new File(targetPath);
+ targetFile.getParentFile().mkdirs(); // ensure directory exists
+ FileOutputStream targetFOS = new FileOutputStream(targetFile);
+ try {
+ ByteBuffer bb = ByteBuffer.allocate((int)sourceFC.size());
+ sourceFC.read(bb);
+ bb.flip(); // ready for reading
+ targetFOS.getChannel().write(bb);
+ } finally {
+ sourceFIS.close();
+ targetFOS.close();
+ }
+ } finally {
+ tt.done();
+ }
+ return load(trans.info(),targetDir,targetFileName,mediaType,0);
+ }
+ }
+
+ public void invalidate(String key) {
+ content.remove(key);
+ }
+
+ private static final Content NULL=new Content() {
+
+ @Override
+ public void setHeader(HttpServletResponse resp) {
+ resp.setStatus(HttpStatus.NOT_FOUND_404);
+ resp.setHeader("Content-type","text/plain");
+ }
+
+ @Override
+ public void write(Writer writer) throws IOException {
+ }
+
+ @Override
+ public void write(OutputStream os) throws IOException {
+ }
+
+ };
+
+ private static abstract class Content {
+ private long date; // date of the actual artifact (i.e. File modified date)
+ private long access; // last accessed
+
+ protected String contentType;
+ protected boolean attachmentOnly;
+
+ public void setHeader(HttpServletResponse resp) {
+ resp.setStatus(HttpStatus.OK_200);
+ resp.setHeader("Content-type",contentType);
+ resp.setHeader("Cache-Control", MAX_AGE);
+ }
+
+ public abstract void write(Writer writer) throws IOException;
+ public abstract void write(OutputStream os) throws IOException;
+
+ }
+
+ private static class DirectFileContent extends Content {
+ private File file;
+ public DirectFileContent(File f) {
+ file = f;
+ }
+
+ public String toString() {
+ return file.getName();
+ }
+
+ public void write(Writer writer) throws IOException {
+ FileReader fr = new FileReader(file);
+ char[] buff = new char[1024];
+ try {
+ int read;
+ while((read = fr.read(buff,0,1024))>=0) {
+ writer.write(buff,0,read);
+ }
+ } finally {
+ fr.close();
+ }
+ }
+
+ public void write(OutputStream os) throws IOException {
+ FileInputStream fis = new FileInputStream(file);
+ byte[] buff = new byte[1024];
+ try {
+ int read;
+ while((read = fis.read(buff,0,1024))>=0) {
+ os.write(buff,0,read);
+ }
+ } finally {
+ fis.close();
+ }
+ }
+
+ }
+ private static class CachedContent extends Content {
+ private byte[] data;
+ private int end;
+ private char[] cdata;
+
+ public CachedContent(File f) throws IOException {
+ // Read and Cache
+ ByteBuffer bb = ByteBuffer.allocate((int)f.length());
+ FileInputStream fis = new FileInputStream(f);
+ try {
+ fis.getChannel().read(bb);
+ } finally {
+ fis.close();
+ }
+
+ data = bb.array();
+ end = bb.position();
+ cdata=null;
+ }
+
+ public String toString() {
+ return data.toString();
+ }
+
+ public void write(Writer writer) throws IOException {
+ synchronized(this) {
+ // do the String Transformation once, and only if actually used
+ if(cdata==null) {
+ cdata = new char[end];
+ new String(data).getChars(0, end, cdata, 0);
+ }
+ }
+ writer.write(cdata,0,end);
+ }
+ public void write(OutputStream os) throws IOException {
+ os.write(data,0,end);
+ }
+
+ }
+
+ public void setEnv(LogTarget env) {
+ logT = env;
+ }
+
+ /**
+ * Cleanup thread to remove older items if max Cache is reached.
+ *
+ */
+ private static class Cleanup extends TimerTask {
+ private int maxSize;
+ private NavigableMap<String, Content> content;
+
+ public Cleanup(NavigableMap<String, Content> content, int size) {
+ maxSize = size;
+ this.content = content;
+ }
+
+ private class Comp implements Comparable<Comp> {
+ public Map.Entry<String, Content> entry;
+
+ public Comp(Map.Entry<String, Content> en) {
+ entry = en;
+ }
+
+ @Override
+ public int compareTo(Comp o) {
+ return (int)(entry.getValue().access-o.entry.getValue().access);
+ }
+
+ }
+ @SuppressWarnings("unchecked")
+ @Override
+ public void run() {
+ int size = content.size();
+ if(size>maxSize) {
+ ArrayList<Comp> scont = new ArrayList<Comp>(size);
+ Object[] entries = content.entrySet().toArray();
+ for(int i=0;i<size;++i) {
+ scont.add(i, new Comp((Map.Entry<String,Content>)entries[i]));
+ }
+ Collections.sort(scont);
+ int end = size - ((maxSize/4)*3); // reduce to 3/4 of max size
+ System.out.println("------ Cleanup Cycle ------ " + new Date().toString() + " -------");
+ for(int i=0;i<end;++i) {
+ Entry<String, Content> entry = scont.get(i).entry;
+ content.remove(entry.getKey());
+ System.out.println("removed Cache Item " + entry.getKey() + "/" + new Date(entry.getValue().access).toString());
+ }
+ for(int i=end;i<size;++i) {
+ Entry<String, Content> entry = scont.get(i).entry;
+ System.out.println("remaining Cache Item " + entry.getKey() + "/" + new Date(entry.getValue().access).toString());
+ }
+ }
+ }
+ }
+}
diff --git a/authz-core/src/main/java/com/att/cssa/rserv/CodeSetter.java b/authz-core/src/main/java/com/att/cssa/rserv/CodeSetter.java
new file mode 100644
index 00000000..01ecf38d
--- /dev/null
+++ b/authz-core/src/main/java/com/att/cssa/rserv/CodeSetter.java
@@ -0,0 +1,54 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.aai
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * Copyright © 2017 Amdocs
+ * * ===========================================================================
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ * * http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ * * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+package com.att.cssa.rserv;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import com.att.inno.env.Trans;
+
+// Package on purpose. only want between RServlet and Routes
+class CodeSetter<TRANS extends Trans> {
+ private HttpCode<TRANS,?> code;
+ private TRANS trans;
+ private HttpServletRequest req;
+ private HttpServletResponse resp;
+ public CodeSetter(TRANS trans, HttpServletRequest req, HttpServletResponse resp) {
+ this.trans = trans;
+ this.req = req;
+ this.resp = resp;
+
+ }
+ public boolean matches(Route<TRANS> route) throws IOException, ServletException {
+ // Find best Code in Route based on "Accepts (Get) or Content-Type" (if exists)
+ return (code = route.getCode(trans, req, resp))!=null;
+ }
+
+ public HttpCode<TRANS,?> code() {
+ return code;
+ }
+}
diff --git a/authz-core/src/main/java/com/att/cssa/rserv/Content.java b/authz-core/src/main/java/com/att/cssa/rserv/Content.java
new file mode 100644
index 00000000..fd6c8a58
--- /dev/null
+++ b/authz-core/src/main/java/com/att/cssa/rserv/Content.java
@@ -0,0 +1,116 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.aai
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * Copyright © 2017 Amdocs
+ * * ===========================================================================
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ * * http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ * * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+package com.att.cssa.rserv;
+
+import java.util.List;
+
+import com.att.inno.env.Trans;
+
+
+
+/**
+ * A Class to hold Service "ContentTypes", and to match incoming "Accept" types from HTTP.
+ *
+ * This is a multi-use class built to use the same Parser for ContentTypes and Accept.
+ *
+ * Thus, you would create and use "Content.Type" within your service, and use it to match
+ * Accept Strings. What is returned is an Integer (for faster processing), which can be
+ * used in a switch statement to act on match different Actions. The server should
+ * know which behaviors match.
+ *
+ * "bestMatch" returns an integer for the best match, or -1 if no matches.
+ *
+ *
+ */
+public abstract class Content<TRANS extends Trans> {
+ public static final String Q = "q";
+ protected abstract Pair<String,Pair<HttpCode<TRANS,?>,List<Pair<String,Object>>>> types(HttpCode<TRANS,?> code, String str);
+ protected abstract boolean props(Pair<String, Pair<HttpCode<TRANS,?>,List<Pair<String,Object>>>> type, String tag, String value);
+
+ /**
+ * Parse a Content-Type/Accept. As found, call "types" and "props", which do different
+ * things depending on if it's a Content-Type or Accepts.
+ *
+ * For Content-Type, it builds a tree suitable for Comparison
+ * For Accepts, it compares against the tree, and builds an acceptable type list
+ *
+ * Since this parse code is used for every incoming HTTP transaction, I have removed the implementation
+ * that uses String.split, and replaced with integers evaluating the Byte array. This results
+ * in only the necessary strings created, resulting in 1/3 better speed, and less
+ * Garbage collection.
+ *
+ * @param trans
+ * @param code
+ * @param cntnt
+ * @return
+ */
+ protected boolean parse(HttpCode<TRANS,?> code, String cntnt) {
+ byte bytes[] = cntnt.getBytes();
+ boolean contType=false,contProp=true;
+ int cis,cie=-1,cend;
+ int sis,sie,send;
+ do {
+ cis = cie+1;
+ cie = cntnt.indexOf(',',cis);
+ cend = cie<0?bytes.length:cie;
+ // Start SEMIS
+ sie=cis-1;
+ Pair<String, Pair<HttpCode<TRANS,?>, List<Pair<String, Object>>>> me = null;
+ do {
+ sis = sie+1;
+ sie = cntnt.indexOf(';',sis);
+ send = sie>cend || sie<0?cend:sie;
+ if(me==null) {
+ String semi = new String(bytes,sis,send-sis);
+ // trans.checkpoint(semi);
+ // Look at first entity within comma group
+ // Is this an acceptable Type?
+ me=types(code, semi);
+ if(me==null) {
+ sie=-1; // skip the rest of the processing... not a type
+ } else {
+ contType=true;
+ }
+ } else { // We've looped past the first Semi, now process as properties
+ // If there are additional elements (more entities within Semi Colons)
+ // apply Propertys
+ int eq = cntnt.indexOf('=',sis);
+ if(eq>sis && eq<send) {
+ String tag = new String(bytes,sis,eq-sis);
+ String value = new String(bytes,eq+1,send-(eq+1));
+ // trans.checkpoint(" Prop " + tag + "=" + value);
+ boolean bool = props(me,tag,value);
+ if(!bool) {
+ contProp=false;
+ }
+ }
+ }
+ // End Property
+ } while(sie<=cend && sie>=cis);
+ // End SEMIS
+ } while(cie>=0);
+ return contType && contProp; // for use in finds, True if a type found AND all props matched
+ }
+
+}
diff --git a/authz-core/src/main/java/com/att/cssa/rserv/HttpCode.java b/authz-core/src/main/java/com/att/cssa/rserv/HttpCode.java
new file mode 100644
index 00000000..ca078431
--- /dev/null
+++ b/authz-core/src/main/java/com/att/cssa/rserv/HttpCode.java
@@ -0,0 +1,112 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.aai
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * Copyright © 2017 Amdocs
+ * * ===========================================================================
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ * * http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ * * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+package com.att.cssa.rserv;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import com.att.inno.env.Trans;
+
+/**
+ * HTTP Code element, which responds to the essential "handle Method".
+ *
+ * Use Native HttpServletRe[quest|sponse] calls for questions like QueryParameters (getParameter, etc)
+ *
+ * Use local "pathParam" method to obtain in an optimized manner the path parameter, which must be interpreted by originating string
+ *
+ * i.e. my/path/:id/:other/*
+ *
+ *
+ * @param <TRANS>
+ * @param <T>
+ */
+public abstract class HttpCode<TRANS extends Trans, CONTEXT> {
+ protected CONTEXT context;
+ private String desc;
+ protected String [] roles;
+ private boolean all;
+
+ // Package by design... Set by Route when linked
+ Match match;
+
+ public HttpCode(CONTEXT context, String description, String ... roles) {
+ this.context = context;
+ desc = description;
+
+ // Evaluate for "*" once...
+ all = false;
+ for(String srole : roles) {
+ if("*".equals(srole)) {
+ all = true;
+ break;
+ }
+ }
+ this.roles = all?null:roles;
+ }
+
+ public abstract void handle(TRANS trans, HttpServletRequest req, HttpServletResponse resp) throws Exception;
+
+ public String desc() {
+ return desc;
+ }
+
+ /**
+ * Get the variable element out of the Path Parameter, as set by initial Code
+ *
+ * @param req
+ * @param key
+ * @return
+ */
+ public String pathParam(HttpServletRequest req, String key) {
+ return match.param(req.getPathInfo(), key);
+ }
+
+ // Note: get Query Params from Request
+
+ /**
+ * Check for Authorization when set.
+ *
+ * If no Roles set, then accepts all users
+ *
+ * @param req
+ * @return
+ */
+ public boolean isAuthorized(HttpServletRequest req) {
+ if(all)return true;
+ if(roles!=null) {
+ for(String srole : roles) {
+ if(req.isUserInRole(srole)) return true;
+ }
+ }
+ return false;
+ }
+
+ public boolean no_cache() {
+ return false;
+ }
+
+ public String toString() {
+ return desc;
+ }
+}
diff --git a/authz-core/src/main/java/com/att/cssa/rserv/HttpMethods.java b/authz-core/src/main/java/com/att/cssa/rserv/HttpMethods.java
new file mode 100644
index 00000000..c247aad0
--- /dev/null
+++ b/authz-core/src/main/java/com/att/cssa/rserv/HttpMethods.java
@@ -0,0 +1,31 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.aai
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * Copyright © 2017 Amdocs
+ * * ===========================================================================
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ * * http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ * * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+package com.att.cssa.rserv;
+
+public enum HttpMethods {
+ POST,
+ GET,
+ PUT,
+ DELETE
+}
diff --git a/authz-core/src/main/java/com/att/cssa/rserv/Match.java b/authz-core/src/main/java/com/att/cssa/rserv/Match.java
new file mode 100644
index 00000000..1f5a60f5
--- /dev/null
+++ b/authz-core/src/main/java/com/att/cssa/rserv/Match.java
@@ -0,0 +1,212 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.aai
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * Copyright © 2017 Amdocs
+ * * ===========================================================================
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ * * http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ * * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+package com.att.cssa.rserv;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * This path matching algorithm avoids using split strings during the critical transactional run-time. By pre-analyzing the
+ * content at "set Param" time, and storing data in an array-index model which presumably is done once and at the beginning,
+ * we can match in much less time when it actually counts.
+ *
+ *
+ */
+public class Match {
+ private Map<String, Integer> params;
+ private byte[] values[];
+ private Integer vars[];
+ private boolean wildcard;
+
+
+ /*
+ * These two methods are pairs of searching performance for variables Spark Style.
+ * setParams evaluates the target path, and sets a HashMap that will return an Integer.
+ * the Keys are both :key and key so that there will be no string operations during
+ * a transaction
+ *
+ * For the Integer, if the High Order is 0, then it is just one value. If High Order >0, then it is
+ * a multi-field option, i.e. ending with a wild-card.
+ */
+ public Match(String path) {
+ // IF DEBUG: System.out.print("\n[" + path + "]");
+ params = new HashMap<String,Integer>();
+ if(path!=null) {
+ String[] pa = path.split("/");
+ values = new byte[pa.length][];
+ vars = new Integer[pa.length];
+
+ int val = 0;
+ String key;
+ for(int i=0;i<pa.length && !wildcard;++i) {
+ if(pa[i].startsWith(":")) {
+ if(pa[i].endsWith("*")) {
+ val = i | pa.length<<16; // load end value in high order bits
+ key = pa[i].substring(0, pa[i].length()-1);// remove *
+ wildcard = true;
+ } else {
+ val = i;
+ key = pa[i];
+ }
+ params.put(key,val); //put in :key
+ params.put(key.substring(1,key.length()), val); // put in just key, better than adding a missing one, like Spark
+ // values[i]=null; // null stands for Variable
+ vars[i]=val;
+ } else {
+ values[i]=pa[i].getBytes();
+ if(pa[i].endsWith("*")) {
+ wildcard = true;
+ if(pa[i].length()>1) {
+ /* remove * from value */
+ int newlength = values[i].length-1;
+ byte[] real = new byte[newlength];
+ System.arraycopy(values[i],0,real,0,newlength);
+ values[i]=real;
+ } else {
+ vars[i]=0; // this is actually a variable, if it only contains a "*"
+ }
+ }
+ // vars[i]=null;
+ }
+ }
+ }
+ }
+
+ /*
+ * This is the second of the param evaluation functions. First, we look up to see if there is
+ * any reference by key in the params Map created by the above.
+ *
+ * The resulting Integer, if not null, is split high/low order into start and end.
+ * We evaluate the string for '/', rather than splitting into String[] to avoid the time/mem needed
+ * We traverse to the proper field number for slash, evaluate the end (whether wild card or no),
+ * and return the substring.
+ *
+ * The result is something less than .003 milliseconds per evaluation
+ *
+ */
+ public String param(String path,String key) {
+ Integer val = params.get(key); // :key or key
+ if(val!=null) {
+ int start = val & 0xFFFF;
+ int end = (val >> 16) & 0xFFFF;
+ int idx = -1;
+ int i;
+ for(i=0;i<start;++i) {
+ idx = path.indexOf('/',idx+1);
+ if(idx<0)break;
+ }
+ if(i==start) {
+ ++idx;
+ if(end==0) {
+ end = path.indexOf('/',idx);
+ if(end<0)end=path.length();
+ } else {
+ end=path.length();
+ }
+ return path.substring(idx,end);
+ } else if(i==start-1) { // if last spot was left blank, i.e. :key*
+ return "";
+ }
+ }
+ return null;
+ }
+
+ public boolean match(String path) {
+ if(path==null|| path.length()==0 || "/".equals(path) ) {
+ if(values==null)return true;
+ switch(values.length) {
+ case 0: return true;
+ case 1: return values[0].length==0;
+ default: return false;
+ }
+ }
+ boolean rv = true;
+ byte[] pabytes = path.getBytes();
+ int field=0;
+ int fieldIdx = 0;
+
+ int lastField = values.length;
+ int lastByte = pabytes.length;
+ boolean fieldMatched = false; // = lastByte>0?(pabytes[0]=='/'):false;
+ // IF DEBUG: System.out.println("\n -- " + path + " --");
+ for(int i=0;rv && i<lastByte;++i) {
+ if(field>=lastField) { // checking here allows there to be a non-functional ending /
+ rv = false;
+ break;
+ }
+ if(values[field]==null) { // it's a variable, just look for /s
+ if(wildcard && field==lastField-1) return true;// we've made it this far. We accept all remaining characters
+ Integer val = vars[field];
+ int start = val & 0xFFFF;
+ int end = (val >> 16) & 0xFFFF;
+ if(end==0)end=start+1;
+ int k = i;
+ for(int j=start; j<end && k<lastByte; ++k) {
+ // IF DEBUG: System.out.print((char)pabytes[k]);
+ if(pabytes[k]=='/') {
+ ++field;
+ ++j;
+ }
+ }
+
+ if(k==lastByte && pabytes[k-1]!='/')++field;
+ if(k>i)i=k-1; // if we've incremented, have to accommodate the outer for loop incrementing as well
+ fieldMatched = false; // reset
+ fieldIdx = 0;
+ } else {
+ // IF DEBUG: System.out.print((char)pabytes[i]);
+ if(pabytes[i]=='/') { // end of field, eval if Field is matched
+ // if double slash, check if supposed to be empty
+ if(fieldIdx==0 && values[field].length==0) {
+ fieldMatched = true;
+ }
+ rv = fieldMatched && ++field<lastField;
+ // reset
+ fieldMatched = false;
+ fieldIdx = 0;
+ } else if(values[field].length==0) {
+ // double slash in path, but content in field. We check specially here to avoid
+ // Array out of bounds issues.
+ rv = false;
+ } else {
+ if(fieldMatched) {
+ rv =false; // field is already matched, now there's too many bytes
+ } else {
+ rv = pabytes[i]==values[field][fieldIdx++]; // compare expected (pabytes[i]) with value for particular field
+ fieldMatched=values[field].length==fieldIdx; // are all the bytes match in the field?
+ if(fieldMatched && (i==lastByte-1 || (wildcard && field==lastField-1)))
+ return true; // last field info
+ }
+ }
+ }
+ }
+ if(field!=lastField || pabytes.length!=lastByte) rv = false; // have we matched all the fields and all the bytes?
+ return rv;
+ }
+
+ public Set<String> getParamNames() {
+ return params.keySet();
+ }
+}
diff --git a/authz-core/src/main/java/com/att/cssa/rserv/Pair.java b/authz-core/src/main/java/com/att/cssa/rserv/Pair.java
new file mode 100644
index 00000000..7d3b88c6
--- /dev/null
+++ b/authz-core/src/main/java/com/att/cssa/rserv/Pair.java
@@ -0,0 +1,44 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.aai
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * Copyright © 2017 Amdocs
+ * * ===========================================================================
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ * * http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ * * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+package com.att.cssa.rserv;
+
+/**
+ * A pair of generic Objects.
+ *
+ * @param <X>
+ * @param <Y>
+ */
+public class Pair<X,Y> {
+ public X x;
+ public Y y;
+
+ public Pair(X x, Y y) {
+ this.x = x;
+ this.y = y;
+ }
+
+ public String toString() {
+ return "X: " + x.toString() + "-->" + y.toString();
+ }
+}
diff --git a/authz-core/src/main/java/com/att/cssa/rserv/RServlet.java b/authz-core/src/main/java/com/att/cssa/rserv/RServlet.java
new file mode 100644
index 00000000..ed116e64
--- /dev/null
+++ b/authz-core/src/main/java/com/att/cssa/rserv/RServlet.java
@@ -0,0 +1,156 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.aai
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * Copyright © 2017 Amdocs
+ * * ===========================================================================
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ * * http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ * * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+package com.att.cssa.rserv;
+
+import java.io.IOException;
+import java.util.List;
+
+import javax.servlet.Servlet;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import com.att.inno.env.Env;
+import com.att.inno.env.TimeTaken;
+import com.att.inno.env.Trans;
+
+public abstract class RServlet<TRANS extends Trans> implements Servlet {
+ private Routes<TRANS> routes = new Routes<TRANS>();
+
+ private ServletConfig config;
+
+ @Override
+ public void init(ServletConfig config) throws ServletException {
+ this.config = config;
+ }
+
+ @Override
+ public ServletConfig getServletConfig() {
+ return config;
+ }
+
+ public void route(Env env, HttpMethods meth, String path, HttpCode<TRANS, ?> code, String ... moreTypes) {
+ Route<TRANS> r = routes.findOrCreate(meth,path);
+ r.add(code,moreTypes);
+ env.init().log(r.report(code),code);
+ }
+
+ @Override
+ public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
+ HttpServletRequest request = (HttpServletRequest)req;
+ HttpServletResponse response = (HttpServletResponse)res;
+
+ @SuppressWarnings("unchecked")
+ TRANS trans = (TRANS)req.getAttribute(TransFilter.TRANS_TAG);
+ if(trans==null) {
+ response.setStatus(404); // Not Found, because it didn't go through TransFilter
+ return;
+ }
+
+ Route<TRANS> route;
+ HttpCode<TRANS,?> code=null;
+ String ct = req.getContentType();
+ TimeTaken tt = trans.start("Resolve to Code", Env.SUB);
+ try {
+ // routes have multiple code sets. This object picks the best code set
+ // based on Accept or Content-Type
+ CodeSetter<TRANS> codesetter = new CodeSetter<TRANS>(trans,request,response);
+ // Find declared route
+ route = routes.derive(request, codesetter);
+ if(route==null) {
+ String method = request.getMethod();
+ trans.checkpoint("No Route matches "+ method + ' ' + request.getPathInfo());
+ response.setStatus(404); // Not Found
+ } else {
+ // Find best Code in Route based on "Accepts (Get) or Content-Type" (if exists)
+ code = codesetter.code();// route.getCode(trans, request, response);
+ }
+ } finally {
+ tt.done();
+ }
+
+ if(route!=null && code!=null) {
+ StringBuilder sb = new StringBuilder(72);
+ sb.append(route.auditText);
+ sb.append(',');
+ sb.append(code.desc());
+ if(ct!=null) {
+ sb.append(", ContentType: ");
+ sb.append(ct);
+ }
+ tt = trans.start(sb.toString(),Env.SUB);
+ try {
+ /*obj = */
+ code.handle(trans, request, response);
+ response.flushBuffer();
+ } catch (ServletException e) {
+ trans.error().log(e);
+ throw e;
+ } catch (Exception e) {
+ trans.error().log(e,request.getMethod(),request.getPathInfo());
+ throw new ServletException(e);
+ } finally {
+ tt.done();
+ }
+ }
+ }
+
+ @Override
+ public String getServletInfo() {
+ return "RServlet for Jetty";
+ }
+
+ @Override
+ public void destroy() {
+ }
+
+ public String applicationJSON(Class<?> cls, String version) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("application/");
+ sb.append(cls.getSimpleName());
+ sb.append("+json");
+ sb.append(";charset=utf-8");
+ sb.append(";version=");
+ sb.append(version);
+ return sb.toString();
+ }
+
+ public String applicationXML(Class<?> cls, String version) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("application/");
+ sb.append(cls.getSimpleName());
+ sb.append("+xml");
+ sb.append(";charset=utf-8");
+ sb.append(";version=");
+ sb.append(version);
+ return sb.toString();
+ }
+
+ public List<RouteReport> routeReport() {
+ return routes.routeReport();
+ }
+}
diff --git a/authz-core/src/main/java/com/att/cssa/rserv/Route.java b/authz-core/src/main/java/com/att/cssa/rserv/Route.java
new file mode 100644
index 00000000..0a8cffe0
--- /dev/null
+++ b/authz-core/src/main/java/com/att/cssa/rserv/Route.java
@@ -0,0 +1,143 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.aai
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * Copyright © 2017 Amdocs
+ * * ===========================================================================
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ * * http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ * * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+package com.att.cssa.rserv;
+
+import java.io.IOException;
+import java.util.List;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import com.att.inno.env.Env;
+import com.att.inno.env.TimeTaken;
+import com.att.inno.env.Trans;
+
+public class Route<TRANS extends Trans> {
+ public final String auditText;
+ public final HttpMethods meth;
+ public final String path;
+
+ private Match match;
+ // package on purpose
+ private final TypedCode<TRANS> content;
+ private final boolean isGet;
+
+ public Route(HttpMethods meth, String path) {
+ this.path = path;
+ auditText = meth.name() + ' ' + path;
+ this.meth = meth; // Note: Using Spark def for now.
+ isGet = meth.compareTo(HttpMethods.GET) == 0;
+ match = new Match(path);
+ content = new TypedCode<TRANS>();
+ }
+
+ public void add(HttpCode<TRANS,?> code, String ... others) {
+ code.match = match;
+ content.add(code, others);
+ }
+
+// public void add(HttpCode<TRANS,?> code, Class<?> cls, String version, String ... others) {
+// code.match = match;
+// content.add(code, cls, version, others);
+// }
+//
+ public HttpCode<TRANS,?> getCode(TRANS trans, HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
+ // Type is associated with Accept for GET (since it is what is being returned
+ // We associate the rest with ContentType.
+ // FYI, thought about this a long time before implementing this way.
+ String compare;
+// String special[]; // todo, expose Charset (in special) to outside
+ if(isGet) {
+ compare = req.getHeader("Accept"); // Accept is used for read, as we want to agree on what caller is ready to handle
+ } else {
+ compare = req.getContentType(); // Content type used to declare what data is being created, updated or deleted (might be used for key)
+ }
+
+ Pair<String, Pair<HttpCode<TRANS, ?>, List<Pair<String, Object>>>> hl = content.prep(trans, compare);
+ if(hl==null) {
+ resp.setStatus(406); // NOT_ACCEPTABLE
+ } else {
+ if(isGet) { // Set Content Type to expected content
+ if("*".equals(hl.x) || "*/*".equals(hl.x)) {// if wild-card, then choose first kind of type
+ resp.setContentType(content.first());
+ } else {
+ resp.setContentType(hl.x);
+ }
+ }
+ return hl.y.x;
+ }
+ return null;
+ }
+
+ public Route<TRANS> matches(String method, String path) {
+ return meth.name().equalsIgnoreCase(method) && match.match(path)?this:null;
+ }
+
+ public TimeTaken start(Trans trans, String auditText, HttpCode<TRANS,?> code, String type) {
+ StringBuilder sb = new StringBuilder(auditText);
+ sb.append(", ");
+ sb.append(code.desc());
+ sb.append(", Content: ");
+ sb.append(type);
+ return trans.start(sb.toString(), Env.SUB);
+ }
+
+ // Package on purpose.. for "find/Create" routes only
+ boolean resolvesTo(HttpMethods hm, String p) {
+ return(path.equals(p) && hm.equals(meth));
+ }
+
+ public String toString() {
+ return auditText + ' ' + content;
+ }
+
+ public String report(HttpCode<TRANS, ?> code) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(auditText);
+ sb.append(' ');
+ content.relatedTo(code, sb);
+ return sb.toString();
+ }
+
+ public RouteReport api() {
+ RouteReport tr = new RouteReport();
+ tr.meth = meth;
+ tr.path = path;
+ content.api(tr);
+ return tr;
+ }
+
+
+ /**
+ * contentRelatedTo (For reporting) list routes that will end up at a specific Code
+ * @return
+ */
+ public String contentRelatedTo(HttpCode<TRANS, ?> code) {
+ StringBuilder sb = new StringBuilder(path);
+ sb.append(' ');
+ content.relatedTo(code, sb);
+ return sb.toString();
+ }
+}
diff --git a/authz-core/src/main/java/com/att/cssa/rserv/RouteReport.java b/authz-core/src/main/java/com/att/cssa/rserv/RouteReport.java
new file mode 100644
index 00000000..6dee8d32
--- /dev/null
+++ b/authz-core/src/main/java/com/att/cssa/rserv/RouteReport.java
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.aai
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * Copyright © 2017 Amdocs
+ * * ===========================================================================
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ * * http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ * * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+package com.att.cssa.rserv;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class RouteReport {
+ public HttpMethods meth;
+ public String path;
+ public String desc;
+ public final List<String> contextTypes = new ArrayList<String>();
+
+}
diff --git a/authz-core/src/main/java/com/att/cssa/rserv/Routes.java b/authz-core/src/main/java/com/att/cssa/rserv/Routes.java
new file mode 100644
index 00000000..c2d7fb82
--- /dev/null
+++ b/authz-core/src/main/java/com/att/cssa/rserv/Routes.java
@@ -0,0 +1,91 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.aai
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * Copyright © 2017 Amdocs
+ * * ===========================================================================
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ * * http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ * * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+package com.att.cssa.rserv;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+
+import com.att.inno.env.Trans;
+
+
+public class Routes<TRANS extends Trans> {
+ // Since this must be very, very fast, and only needs one creation, we'll use just an array.
+ private Route<TRANS>[] routes;
+ private int end;
+
+
+ @SuppressWarnings("unchecked")
+ public Routes() {
+ routes = new Route[10];
+ end = 0;
+ }
+
+ // This method for setup of Routes only...
+ // Package on purpose
+ synchronized Route<TRANS> findOrCreate(HttpMethods meth, String path) {
+ Route<TRANS> rv = null;
+ for(int i=0;i<end;++i) {
+ if(routes[i].resolvesTo(meth,path))rv = routes[i];
+ }
+
+ if(rv==null) {
+ if(end>=routes.length) {
+ @SuppressWarnings("unchecked")
+ Route<TRANS>[] temp = new Route[end+10];
+ System.arraycopy(routes, 0, temp, 0, routes.length);
+ routes = temp;
+ }
+
+ routes[end++]=rv=new Route<TRANS>(meth,path);
+ }
+ return rv;
+ }
+
+ public Route<TRANS> derive(HttpServletRequest req, CodeSetter<TRANS> codeSetter) throws IOException, ServletException {
+ Route<TRANS> rv = null;
+ String path = req.getPathInfo();
+ String meth = req.getMethod();
+ //TODO a TREE would be better
+ for(int i=0;rv==null && i<end; ++i) {
+ rv = routes[i].matches(meth,path);
+ if(rv!=null && !codeSetter.matches(rv)) { // potential match, check if has Code
+ rv = null; // not quite, keep going
+ }
+ }
+ //TODO a Default?
+ return rv;
+ }
+
+ public List<RouteReport> routeReport() {
+ ArrayList<RouteReport> ltr = new ArrayList<RouteReport>();
+ for(int i=0;i<end;++i) {
+ ltr.add(routes[i].api());
+ }
+ return ltr;
+ }
+}
diff --git a/authz-core/src/main/java/com/att/cssa/rserv/TransFilter.java b/authz-core/src/main/java/com/att/cssa/rserv/TransFilter.java
new file mode 100644
index 00000000..244609bc
--- /dev/null
+++ b/authz-core/src/main/java/com/att/cssa/rserv/TransFilter.java
@@ -0,0 +1,137 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.aai
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * Copyright © 2017 Amdocs
+ * * ===========================================================================
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ * * http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ * * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+package com.att.cssa.rserv;
+
+import java.io.IOException;
+import java.security.Principal;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import com.att.cadi.Access;
+import com.att.cadi.CadiException;
+import com.att.cadi.CadiWrap;
+import com.att.cadi.Connector;
+import com.att.cadi.Lur;
+import com.att.cadi.TrustChecker;
+import com.att.cadi.filter.CadiHTTPManip;
+import com.att.cadi.taf.TafResp;
+import com.att.cadi.taf.TafResp.RESP;
+import com.att.inno.env.Env;
+import com.att.inno.env.TimeTaken;
+import com.att.inno.env.TransStore;
+
+/**
+ * Create a new Transaction Object for each and every incoming Transaction
+ *
+ * Attach to Request. User "FilterHolder" mechanism to retain single instance.
+ *
+ * TransFilter includes CADIFilter as part of the package, so that it can
+ * set User Data, etc, as necessary.
+ *
+ *
+ */
+public abstract class TransFilter<TRANS extends TransStore> implements Filter {
+ public static final String TRANS_TAG = "__TRANS__";
+
+ private CadiHTTPManip cadi;
+
+ public TransFilter(Access access, Connector con, TrustChecker tc, Object ... additionalTafLurs) throws CadiException {
+ cadi = new CadiHTTPManip(access, con, tc, additionalTafLurs);
+ }
+
+ @Override
+ public void init(FilterConfig filterConfig) throws ServletException {
+ }
+
+ protected Lur getLur() {
+ return cadi.getLur();
+ }
+
+ protected abstract TRANS newTrans();
+ protected abstract TimeTaken start(TRANS trans, ServletRequest request);
+ protected abstract void authenticated(TRANS trans, Principal p);
+ protected abstract void tallyHo(TRANS trans);
+
+ @Override
+ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
+ TRANS trans = newTrans();
+
+ TimeTaken overall = start(trans,request);
+ try {
+ request.setAttribute(TRANS_TAG, trans);
+
+ HttpServletRequest req = (HttpServletRequest)request;
+ HttpServletResponse res = (HttpServletResponse)response;
+
+ TimeTaken security = trans.start("CADI Security", Env.SUB);
+// TimeTaken ttvalid;
+ TafResp resp;
+ RESP r;
+ CadiWrap cw = null;
+ try {
+ resp = cadi.validate(req,res);
+ switch(r=resp.isAuthenticated()) {
+ case IS_AUTHENTICATED:
+ cw = new CadiWrap(req,resp,cadi.getLur());
+ authenticated(trans, cw.getUserPrincipal());
+ break;
+ default:
+ break;
+ }
+ } finally {
+ security.done();
+ }
+
+ if(r==RESP.IS_AUTHENTICATED) {
+ trans.checkpoint(resp.desc());
+ chain.doFilter(cw, response);
+ } else {
+ //TODO this is a good place to check if too many checks recently
+ // Would need Cached Counter objects that are cleaned up on
+ // use
+ trans.checkpoint(resp.desc(),Env.ALWAYS);
+ if(resp.isFailedAttempt())
+ trans.audit().log(resp.desc());
+ }
+ } catch(Exception e) {
+ trans.error().log(e);
+ trans.checkpoint("Error: " + e.getClass().getSimpleName() + ": " + e.getMessage());
+ throw new ServletException(e);
+ } finally {
+ overall.done();
+ tallyHo(trans);
+ }
+ }
+
+ @Override
+ public void destroy() {
+ };
+}
diff --git a/authz-core/src/main/java/com/att/cssa/rserv/TransOnlyFilter.java b/authz-core/src/main/java/com/att/cssa/rserv/TransOnlyFilter.java
new file mode 100644
index 00000000..1ef5456a
--- /dev/null
+++ b/authz-core/src/main/java/com/att/cssa/rserv/TransOnlyFilter.java
@@ -0,0 +1,78 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.aai
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * Copyright © 2017 Amdocs
+ * * ===========================================================================
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ * * http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ * * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+package com.att.cssa.rserv;
+
+import java.io.IOException;
+import java.security.Principal;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+
+import com.att.inno.env.TimeTaken;
+import com.att.inno.env.TransStore;
+
+/**
+ * Create a new Transaction Object for each and every incoming Transaction
+ *
+ * Attach to Request. User "FilterHolder" mechanism to retain single instance.
+ *
+ * TransFilter includes CADIFilter as part of the package, so that it can
+ * set User Data, etc, as necessary.
+ *
+ *
+ */
+public abstract class TransOnlyFilter<TRANS extends TransStore> implements Filter {
+ @Override
+ public void init(FilterConfig filterConfig) throws ServletException {
+ }
+
+
+
+ protected abstract TRANS newTrans();
+ protected abstract TimeTaken start(TRANS trans, ServletRequest request);
+ protected abstract void authenticated(TRANS trans, Principal p);
+ protected abstract void tallyHo(TRANS trans);
+
+ @Override
+ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
+ TRANS trans = newTrans();
+
+ TimeTaken overall = start(trans,request);
+ try {
+ request.setAttribute(TransFilter.TRANS_TAG, trans);
+ chain.doFilter(request, response);
+ } finally {
+ overall.done();
+ }
+ tallyHo(trans);
+ }
+
+ @Override
+ public void destroy() {
+ };
+}
diff --git a/authz-core/src/main/java/com/att/cssa/rserv/TypedCode.java b/authz-core/src/main/java/com/att/cssa/rserv/TypedCode.java
new file mode 100644
index 00000000..6fd7049a
--- /dev/null
+++ b/authz-core/src/main/java/com/att/cssa/rserv/TypedCode.java
@@ -0,0 +1,269 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.aai
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * Copyright © 2017 Amdocs
+ * * ===========================================================================
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ * * http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ * * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+package com.att.cssa.rserv;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+import javax.servlet.ServletException;
+
+import com.att.inno.env.Env;
+import com.att.inno.env.TimeTaken;
+import com.att.inno.env.Trans;
+
+
+/**
+ * TypedCode organizes implementation code based on the Type and Version of code it works with so that it can
+ * be located quickly at runtime based on the "Accept" HTTP Header.
+ *
+ * FYI: For those in the future wondering why I would create a specialized set of "Pair" for the data content:
+ * 1) TypeCode is used in Route, and this code is used for every transaction... it needs to be blazingly fast
+ * 2) The actual number of objects accessed is quite small and built at startup. Arrays are best
+ * 3) I needed a small, well defined tree where each level is a different Type. Using a "Pair" Generic definitions,
+ * I created type-safety at each level, which you can't get from a TreeSet, etc.
+ * 4) Chaining through the Network is simply object dereferencing, which is as fast as Java can go.
+ * 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
+ * read both in code, and in the debugger. However, TypeSafety allows your IDE (Eclipse) to help you make the
+ * choices. Also, make sure you have a good "toString()" method on each object so you can see what's happening
+ * in the IDE Debugger.
+ *
+ * Empirically, this method of obtaining routes proved to be much faster than the HashSet implementations available in otherwise
+ * competent Open Source.
+ *
+ * @param <TRANS>
+ */
+public class TypedCode<TRANS extends Trans> extends Content<TRANS> {
+ private List<Pair<String, Pair<HttpCode<TRANS,?>,List<Pair<String, Object>>>>> types;
+
+ public TypedCode() {
+ types = new ArrayList<Pair<String,Pair<HttpCode<TRANS,?>,List<Pair<String,Object>>>>>();
+ }
+
+ /**
+ * Construct Typed Code based on ContentType parameters passed in
+ *
+ * @param code
+ * @param others
+ * @return
+ */
+ public TypedCode<TRANS> add(HttpCode<TRANS,?> code, String ... others) {
+ StringBuilder sb = new StringBuilder();
+ boolean first = true;
+ for(String str : others) {
+ if(first) {
+ first = false;
+ } else {
+ sb.append(',');
+ }
+ sb.append(str);
+ }
+ parse(code, sb.toString());
+
+ return this;
+ }
+
+ @Override
+ protected Pair<String, Pair<HttpCode<TRANS,?>, List<Pair<String, Object>>>> types(HttpCode<TRANS,?> code, String str) {
+ Pair<String, Pair<HttpCode<TRANS,?>,List<Pair<String, Object>>>> type = null;
+ ArrayList<Pair<String, Object>> props = new ArrayList<Pair<String,Object>>();
+ // Want Q percentage is to be first in the array everytime. If not listed, 1.0 is default
+ props.add(new Pair<String,Object>(Q,1f));
+ Pair<HttpCode<TRANS,?>, List<Pair<String,Object>>> cl = new Pair<HttpCode<TRANS,?>, List<Pair<String,Object>>>(code, props);
+// // breakup "plus" stuff, i.e. application/xaml+xml
+// int plus = str.indexOf('+');
+// if(plus<0) {
+ type = new Pair<String, Pair<HttpCode<TRANS,?>,List<Pair<String,Object>>>>(str, cl);
+ types.add(type);
+ return type;
+// } else {
+// int prev = str.indexOf('/')+1;
+// String first = str.substring(0,prev);
+// String nstr;
+// while(prev!=0) {
+// nstr = first + (plus>-1?str.substring(prev,plus):str.substring(prev));
+// type = new Pair<String, Pair<HttpCode<TRANS,?>,List<Pair<String,Object>>>>(nstr, cl);
+// types.add(type);
+// prev = plus+1;
+// plus = str.indexOf('+',prev);
+// }
+// return type;
+// }
+ }
+
+ @Override
+ protected boolean props(Pair<String, Pair<HttpCode<TRANS,?>, List<Pair<String, Object>>>> type, String tag, String value) {
+ if(tag.equals(Q)) { // reset the Q value (first in array)
+ boolean rv = true;
+ try {
+ type.y.y.get(0).y=Float.parseFloat(value);
+ return rv;
+ } catch (NumberFormatException e) {
+ rv=false; // Note: this awkward syntax forced by Sonar, which doesn't like doing nothing with Exception
+ // which is what should happen
+ }
+ }
+ return type.y.y.add(new Pair<String,Object>(tag,"version".equals(tag)?new Version(value):value));
+ }
+
+ public Pair<String, Pair<HttpCode<TRANS, ?>, List<Pair<String, Object>>>> prep(TRANS trans, String compare) throws IOException, ServletException {
+ Pair<String, Pair<HttpCode<TRANS,?>, List<Pair<String, Object>>>> c,rv=null;
+ if(types.size()==1 && "".equals((c=types.get(0)).x)) { // if there are no checks for type, skip
+ rv = c;
+ } else {
+ if(compare==null || compare.length()==0) {
+ rv = types.get(0); // first code is used
+ } else {
+ Acceptor<TRANS> acc = new Acceptor<TRANS>(types);
+ boolean accepted;
+ TimeTaken tt = trans.start(compare, Env.SUB);
+ try {
+ accepted = acc.parse(null, compare);
+ } finally {
+ tt.done();
+ }
+ if(accepted) {
+ switch(acc.acceptable.size()) {
+ case 0:
+// // TODO best Status Code?
+// resp.setStatus(HttpStatus.NOT_ACCEPTABLE_406);
+ break;
+ case 1:
+ rv = acc.acceptable.get(0);
+ break;
+ default: // compare Q values to get Best Match
+ float bestQ = -1.0f;
+ Pair<String, Pair<HttpCode<TRANS,?>, List<Pair<String, Object>>>> bestT = null;
+ for(Pair<String, Pair<HttpCode<TRANS,?>, List<Pair<String, Object>>>> type : acc.acceptable) {
+ Float f = (Float)type.y.y.get(0).y; // first property is always Q
+ if(f>bestQ) {
+ bestQ=f;
+ bestT = type;
+ }
+ }
+ if(bestT!=null) {
+ // When it is a GET, the matched type is what is returned, so set ContentType
+// if(isGet)resp.setContentType(bestT.x); // set ContentType of Code<TRANS,?>
+// rv = bestT.y.x;
+ rv = bestT;
+ }
+ }
+ } else {
+ trans.checkpoint("No Match found for Accept");
+ }
+ }
+ }
+ return rv;
+ }
+
+ /**
+ * Print on String Builder content related to specific Code
+ *
+ * This is for Reporting and Debugging purposes, so the content is not cached.
+ *
+ * If code is "null", then all content is matched
+ *
+ * @param code
+ * @return
+ */
+ public StringBuilder relatedTo(HttpCode<TRANS, ?> code, StringBuilder sb) {
+ boolean first = true;
+ for(Pair<String, Pair<HttpCode<TRANS, ?>, List<Pair<String, Object>>>> pair : types) {
+ if(code==null || pair.y.x == code) {
+ if(first) {
+ first = false;
+ } else {
+ sb.append(',');
+ }
+ sb.append(pair.x);
+ for(Pair<String,Object> prop : pair.y.y) {
+ // Don't print "Q". it's there for internal use, but it is only meaningful for "Accepts"
+ if(!prop.x.equals(Q) || !prop.y.equals(1f) ) {
+ sb.append(';');
+ sb.append(prop.x);
+ sb.append('=');
+ sb.append(prop.y);
+ }
+ }
+ }
+ }
+ return sb;
+ }
+
+ public List<Pair<String, Object>> getContent(HttpCode<TRANS,?> code) {
+ for(Pair<String, Pair<HttpCode<TRANS, ?>, List<Pair<String, Object>>>> pair : types) {
+ if(pair.y.x == code) {
+ return pair.y.y;
+ }
+ }
+ return null;
+ }
+
+ public String toString() {
+ return relatedTo(null,new StringBuilder()).toString();
+ }
+
+ public void api(RouteReport tr) {
+ // Need to build up a map, because Prop entries can be in several places.
+ HashMap<HttpCode<?,?>,StringBuilder> psb = new HashMap<HttpCode<?,?>,StringBuilder>();
+ StringBuilder temp;
+ tr.desc = null;
+
+ // Read through Code/TypeCode trees for all accepted Typecodes
+ for(Pair<String, Pair<HttpCode<TRANS, ?>, List<Pair<String, Object>>>> tc : types) {
+ // If new, then it's new Code set, create prefix content
+ if((temp=psb.get(tc.y.x))==null) {
+ psb.put(tc.y.x,temp=new StringBuilder());
+ if(tr.desc==null) {
+ tr.desc = tc.y.x.desc();
+ }
+ } else {
+ temp.append(',');
+ }
+ temp.append(tc.x);
+
+ // add all properties
+ for(Pair<String, Object> props : tc.y.y) {
+ temp.append(';');
+ temp.append(props.x);
+ temp.append('=');
+ temp.append(props.y);
+ }
+ }
+ // Gather all ContentType possibilities for the same code together
+
+ for(StringBuilder sb : psb.values()) {
+ tr.contextTypes.add(sb.toString());
+ }
+ }
+
+ public String first() {
+ if(types.size()>0) {
+ return types.get(0).x;
+ }
+ return null;
+ }
+
+ }
diff --git a/authz-core/src/main/java/com/att/cssa/rserv/Version.java b/authz-core/src/main/java/com/att/cssa/rserv/Version.java
new file mode 100644
index 00000000..e24c48eb
--- /dev/null
+++ b/authz-core/src/main/java/com/att/cssa/rserv/Version.java
@@ -0,0 +1,94 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.aai
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * Copyright © 2017 Amdocs
+ * * ===========================================================================
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ * * http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ * * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+package com.att.cssa.rserv;
+
+
+/**
+ * Analyze and hold Version information for Code
+ *
+ *
+ */
+public class Version {
+ private Object[] parts;
+
+ public Version(String v) {
+ String sparts[] = v.split("\\.");
+ parts = new Object[sparts.length];
+ System.arraycopy(sparts, 0, parts, 0, sparts.length);
+ if(parts.length>1) { // has at least a minor
+ try {
+ parts[1]=Integer.decode(sparts[1]); // minor elements need to be converted to Integer for comparison
+ } catch (NumberFormatException e) {
+ // it's ok, leave it as a string
+ parts[1]=sparts[1]; // This useless piece of code forced by Sonar which calls empty Exceptions "Blockers".
+ }
+ }
+ }
+
+ public boolean equals(Object obj) {
+ if(obj instanceof Version) {
+ Version ver = (Version)obj;
+ int length = Math.min(parts.length, ver.parts.length);
+ for(int i=0;i<length;++i) { // match on declared parts
+ if(i==1) {
+ if(parts[1] instanceof Integer && ver.parts[1] instanceof Integer) {
+ // Match on Minor version if this Version is less than Version to be checked
+ if(((Integer)parts[1])<((Integer)ver.parts[1])) {
+ return false;
+ }
+ continue; // don't match next line
+ }
+ }
+ if(!parts[i].equals(ver.parts[i])) {
+ return false; // other spots exact match
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return super.hashCode();
+ }
+
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ boolean first = true;
+ for(Object obj : parts) {
+ if(first) {
+ first = false;
+ } else {
+ sb.append('.');
+ }
+ sb.append(obj.toString());
+ }
+ return sb.toString();
+ }
+}
diff --git a/authz-core/src/main/java/com/att/cssa/rserv/doc/ApiDoc.java b/authz-core/src/main/java/com/att/cssa/rserv/doc/ApiDoc.java
new file mode 100644
index 00000000..76e589a9
--- /dev/null
+++ b/authz-core/src/main/java/com/att/cssa/rserv/doc/ApiDoc.java
@@ -0,0 +1,43 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.aai
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * Copyright © 2017 Amdocs
+ * * ===========================================================================
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ * * http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ * * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+package com.att.cssa.rserv.doc;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import com.att.cssa.rserv.HttpMethods;
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD})
+public @interface ApiDoc {
+ HttpMethods method();
+ String path();
+ int expectedCode();
+ int[] errorCodes();
+ String[] text();
+ /** Format with name|type|[true|false] */
+ String[] params();
+
+}