--- /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 org.onap.aaf.cmd;\r
+\r
+import java.io.PrintWriter;\r
+import java.io.StringReader;\r
+import java.sql.Date;\r
+import java.text.DateFormat;\r
+import java.text.SimpleDateFormat;\r
+import java.util.ArrayList;\r
+import java.util.Comparator;\r
+import java.util.GregorianCalendar;\r
+import java.util.List;\r
+import java.util.Stack;\r
+import java.util.concurrent.ConcurrentHashMap;\r
+\r
+import org.onap.aaf.cssa.rserv.HttpMethods;\r
+\r
+import org.onap.aaf.cadi.CadiException;\r
+import org.onap.aaf.cadi.LocatorException;\r
+import org.onap.aaf.cadi.client.Future;\r
+import org.onap.aaf.cadi.client.Rcli;\r
+import org.onap.aaf.cadi.client.Retryable;\r
+import org.onap.aaf.cadi.http.HMangr;\r
+import org.onap.aaf.inno.env.APIException;\r
+import org.onap.aaf.inno.env.Data.TYPE;\r
+import org.onap.aaf.inno.env.Env;\r
+import org.onap.aaf.inno.env.util.Chrono;\r
+import org.onap.aaf.rosetta.env.RosettaDF;\r
+import org.onap.aaf.rosetta.env.RosettaEnv;\r
+\r
+import aaf.v2_0.Error;\r
+import aaf.v2_0.History;\r
+import aaf.v2_0.History.Item;\r
+import aaf.v2_0.Request;\r
+\r
+\r
+public abstract class Cmd {\r
+ private static final String AAF_DEFAULT_REALM = "aaf_default_realm";\r
+ \r
+ private static final DateFormat dateFmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");\r
+ protected static final String BLANK = "";\r
+ protected static final String COMMA = ","; // for use in splits\r
+\r
+ protected static final int lineLength = 80;\r
+\r
+ private final static String hformat = "%-23s %-5s %-20s %-35s\n";\r
+\r
+ public static final String STARTDATE = "startdate";\r
+ public static final String ENDDATE = "enddate";\r
+ \r
+ private String name;\r
+ private final Param[] params;\r
+ private int required;\r
+ protected final Cmd parent;\r
+ protected final List<Cmd> children;\r
+ private final ConcurrentHashMap<Class<?>,RosettaDF<?>> dfs = new ConcurrentHashMap<Class<?>,RosettaDF<?>>();\r
+ public final AAFcli aafcli;\r
+ protected Env env;\r
+\r
+ public Cmd(AAFcli aafcli, String name, Param ... params) {\r
+ this(aafcli,null, name,params);\r
+ }\r
+\r
+ public Cmd(Cmd parent, String name, Param ... params) {\r
+ this(parent.aafcli,parent, name,params);\r
+ }\r
+\r
+ Cmd(AAFcli aafcli, Cmd parent, String name, Param ... params) {\r
+ this.parent = parent;\r
+ this.aafcli = aafcli;\r
+ this.env = aafcli.env;\r
+ if(parent!=null) {\r
+ parent.children.add(this);\r
+ }\r
+ children = new ArrayList<Cmd>();\r
+ this.params = params;\r
+ this.name = name;\r
+ required=0;\r
+ for(Param p : params) {\r
+ if(p.required) {\r
+ ++required;\r
+ }\r
+ }\r
+ }\r
+ \r
+ public final int exec(int idx, String ... args) throws CadiException, APIException, LocatorException {\r
+ if(args.length-idx<required) {\r
+ throw new CadiException(build(new StringBuilder("Too few args: "),null).toString());\r
+ }\r
+ return _exec(idx,args);\r
+ }\r
+ \r
+ protected abstract int _exec(int idx, final String ... args) throws CadiException, APIException, LocatorException;\r
+ \r
+ public void detailedHelp(int indent,StringBuilder sb) {\r
+ }\r
+\r
+ protected void detailLine(StringBuilder sb, int length, String s) {\r
+ multiChar(sb,length,' ',0);\r
+ sb.append(s);\r
+ }\r
+\r
+ public void apis(int indent,StringBuilder sb) {\r
+ }\r
+\r
+ protected void api(StringBuilder sb, int _indent, HttpMethods hmeth, String pathInfo, Class<?> cls,boolean head) {\r
+ int indent = _indent;\r
+ final String meth = hmeth.name();\r
+ if(head) {\r
+ sb.append('\n');\r
+ detailLine(sb,indent,"APIs:");\r
+ }\r
+ indent+=2;\r
+ multiChar(sb,indent,' ',0);\r
+ sb.append(meth);\r
+ sb.append(' ');\r
+ sb.append(pathInfo);\r
+ String cliString = aafcli.typeString(cls,true);\r
+ if(indent+meth.length()+pathInfo.length()+cliString.length()+2>80) {\r
+ sb.append(" ...");\r
+ multiChar(sb,indent+3+meth.length(),' ',0);\r
+ } else { // same line\r
+ sb.append(' ');\r
+ }\r
+ sb.append(cliString);\r
+ }\r
+\r
+ protected void multiChar(StringBuilder sb, int length, char c, int indent) {\r
+ sb.append('\n');\r
+ for(int i=0;i<indent;++i)sb.append(' ');\r
+ for(int i=indent;i<length;++i)sb.append(c);\r
+ }\r
+\r
+ public StringBuilder build(StringBuilder sb, StringBuilder detail) {\r
+ if(name!=null) {\r
+ sb.append(name);\r
+ sb.append(' ');\r
+ }\r
+ int line = sb.lastIndexOf("\n")+1;\r
+ if(line<0) {\r
+ line=0;\r
+ }\r
+ int indent = sb.length()-line;\r
+ for(Param p : params) {\r
+ sb.append(p.required?'<':'[');\r
+ sb.append(p.tag);\r
+ sb.append(p.required?"> ": "] ");\r
+ }\r
+ \r
+ boolean first = true;\r
+ for(Cmd child : children) {\r
+ if(first) {\r
+ first = false;\r
+ } else if(detail==null) {\r
+ multiChar(sb,indent,' ',0);\r
+ } else {\r
+ // Write parents for Detailed Report\r
+ Stack<String> stack = new Stack<String>();\r
+ for(Cmd c = child.parent;c!=null;c=c.parent) {\r
+ if(c.name!=null) {\r
+ stack.push(c.name);\r
+ }\r
+ }\r
+ if(!stack.isEmpty()) {\r
+ sb.append(" ");\r
+ while(!stack.isEmpty()) {\r
+ sb.append(stack.pop());\r
+ sb.append(' ');\r
+ }\r
+ }\r
+ }\r
+ child.build(sb,detail);\r
+ if(detail!=null) {\r
+ child.detailedHelp(4, detail);\r
+ // If Child wrote something, then add, bracketing by lines\r
+ if(detail.length()>0) {\r
+ multiChar(sb,80,'-',2);\r
+ sb.append(detail);\r
+ sb.append('\n');\r
+ multiChar(sb,80,'-',2);\r
+ sb.append('\n');\r
+ detail.setLength(0); // reuse\r
+ } else {\r
+ sb.append('\n');\r
+ }\r
+ }\r
+ }\r
+ return sb;\r
+ }\r
+ \r
+ protected void error(Future<?> future) {\r
+ StringBuilder sb = new StringBuilder("Failed");\r
+ String desc = future.body();\r
+ int code = future.code();\r
+ if(desc==null || desc.length()==0) {\r
+ withCode(sb,code);\r
+ } else if(desc.startsWith("{")) {\r
+ StringReader sr = new StringReader(desc);\r
+ try {\r
+ // Note: 11-18-2013. This rather convoluted Message Structure required by TSS Restful Specs, reflecting "Northbound" practices.\r
+ Error err = getDF(Error.class).newData().in(TYPE.JSON).load(sr).asObject();\r
+ sb.append(" [");\r
+ sb.append(err.getMessageId());\r
+ sb.append("]: ");\r
+ String messageBody = err.getText();\r
+ List<String> vars = err.getVariables();\r
+ int pipe;\r
+ for (int varCounter=0;varCounter<vars.size();) {\r
+ String var = vars.get(varCounter);\r
+ ++varCounter;\r
+ if (messageBody.indexOf("%" + varCounter) >= 0) {\r
+ if((pipe = var.indexOf('|'))>=0) { // In AAF, we use a PIPE for Choice\r
+ if (aafcli.isTest()) {\r
+ String expiresStr = var.substring(pipe);\r
+ var = var.replace(expiresStr, "[Placeholder]");\r
+ } else {\r
+ StringBuilder varsb = new StringBuilder(var);\r
+ varsb.deleteCharAt(pipe);\r
+ var = varsb.toString();\r
+ }\r
+ messageBody = messageBody.replace("%" + varCounter, varCounter-1 + ") " + var);\r
+ } else {\r
+ messageBody = messageBody.replace("%" + varCounter, var);\r
+ }\r
+ }\r
+ }\r
+ sb.append(messageBody);\r
+ } catch (Exception e) {\r
+ withCode(sb,code);\r
+ sb.append(" (Note: Details cannot be obtained from Error Structure)");\r
+ }\r
+ } else if(desc.startsWith("<html>")){ // Core Jetty, etc sends HTML for Browsers\r
+ withCode(sb,code);\r
+ } else {\r
+ sb.append(" with code ");\r
+ sb.append(code);\r
+ sb.append(", ");\r
+ sb.append(desc);\r
+ }\r
+ pw().println(sb);\r
+ }\r
+\r
+ \r
+ private void withCode(StringBuilder sb, Integer code) {\r
+ sb.append(" with code ");\r
+ sb.append(code);\r
+ switch(code) {\r
+ case 401:\r
+ sb.append(" (HTTP Not Authenticated)");\r
+ break;\r
+ case 403:\r
+ sb.append(" (HTTP Forbidden)");\r
+ break;\r
+ case 404:\r
+ sb.append(" (HTTP Not Found)");\r
+ break;\r
+ default:\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Consistently set start and end dates from Requests (all derived from Request)\r
+ * @param req\r
+ */\r
+ protected void setStartEnd(Request req) {\r
+ // Set Start/End Dates, if exist\r
+ String str;\r
+ if((str = env.getProperty(Cmd.STARTDATE,null))!=null) {\r
+ req.setStart(Chrono.timeStamp(Date.valueOf(str)));\r
+ }\r
+ \r
+ if((str = env.getProperty(Cmd.ENDDATE,null))!=null) {\r
+ req.setEnd(Chrono.timeStamp(Date.valueOf(str)));\r
+ }\r
+ }\r
+\r
+ @SuppressWarnings("unchecked")\r
+ protected<T> RosettaDF<T> getDF(Class<T> cls) throws APIException {\r
+ RosettaDF<T> rdf = (RosettaDF<T>)dfs.get(cls);\r
+ if(rdf == null) {\r
+ rdf = env().newDataFactory(cls);\r
+ dfs.put(cls, rdf);\r
+ }\r
+ return rdf;\r
+ }\r
+\r
+ public void activity(History history, String header) {\r
+ if (history.getItem().isEmpty()) {\r
+ int start = header.indexOf('[');\r
+ if (start >= 0) {\r
+ pw().println("No Activity Found for " + header.substring(start));\r
+ }\r
+ } else {\r
+ pw().println(header);\r
+ for(int i=0;i<lineLength;++i)pw().print('-');\r
+ pw().println();\r
+ \r
+ pw().format(hformat,"Date","Table","User","Memo");\r
+ for(int i=0;i<lineLength;++i)pw().print('-');\r
+ pw().println();\r
+ \r
+ // Save Server time by Sorting locally\r
+ List<Item> items = history.getItem();\r
+ java.util.Collections.sort(items, new Comparator<Item>() {\r
+ @Override\r
+ public int compare(Item o1, Item o2) {\r
+ return o2.getTimestamp().compare(o1.getTimestamp());\r
+ }\r
+ });\r
+ \r
+ for(History.Item item : items) {\r
+ GregorianCalendar gc = item.getTimestamp().toGregorianCalendar();\r
+ pw().format(hformat,\r
+ dateFmt.format(gc.getTime()),\r
+ item.getTarget(),\r
+ item.getUser(),\r
+ item.getMemo());\r
+ }\r
+ }\r
+ }\r
+ \r
+ /**\r
+ * Turn String Array into a | delimited String\r
+ * @param options\r
+ * @return\r
+ */\r
+ public static String optionsToString(String[] options) {\r
+ StringBuilder sb = new StringBuilder();\r
+ boolean first = true;\r
+ for(String s : options) {\r
+ if(first) {\r
+ first = false;\r
+ } else {\r
+ sb.append('|');\r
+ }\r
+ sb.append(s);\r
+ }\r
+ return sb.toString();\r
+ }\r
+ \r
+ /**\r
+ * return which index number the Option matches.\r
+ * \r
+ * throws an Exception if not part of this Option Set\r
+ * \r
+ * @param options\r
+ * @param test\r
+ * @return\r
+ * @throws Exception\r
+ */\r
+ public int whichOption(String[] options, String test) throws CadiException {\r
+ for(int i=0;i<options.length;++i) {\r
+ if(options[i].equals(test)) {\r
+ return i;\r
+ }\r
+ }\r
+ throw new CadiException(build(new StringBuilder("Invalid Option: "),null).toString());\r
+ }\r
+\r
+ protected RosettaEnv env() {\r
+ return aafcli.env;\r
+ }\r
+\r
+ protected HMangr hman() {\r
+ return aafcli.hman;\r
+ }\r
+\r
+ public<RET> RET same(Retryable<RET> retryable) throws APIException, CadiException, LocatorException {\r
+ // We're storing in AAFCli, because we know it's always the same, and single threaded\r
+ if(aafcli.prevCall!=null) {\r
+ retryable.item(aafcli.prevCall.item());\r
+ retryable.lastClient=aafcli.prevCall.lastClient;\r
+ }\r
+ \r
+ RET ret = aafcli.hman.same(aafcli.ss,retryable);\r
+ \r
+ // Store last call in AAFcli, because Cmds are all different instances.\r
+ aafcli.prevCall = retryable;\r
+ return ret;\r
+ }\r
+\r
+ public<RET> RET all(Retryable<RET> retryable) throws APIException, CadiException, LocatorException {\r
+ this.setQueryParamsOn(retryable.lastClient);\r
+ return aafcli.hman.all(aafcli.ss,retryable);\r
+ }\r
+\r
+ public<RET> RET oneOf(Retryable<RET> retryable,String host) throws APIException, CadiException, LocatorException {\r
+ this.setQueryParamsOn(retryable.lastClient);\r
+ return aafcli.hman.oneOf(aafcli.ss,retryable,true,host);\r
+ }\r
+\r
+ protected PrintWriter pw() {\r
+ return AAFcli.pw;\r
+ }\r
+\r
+ public String getName() {\r
+ return name;\r
+ }\r
+ \r
+ public void reportHead(String ... str) {\r
+ pw().println();\r
+ boolean first = true;\r
+ int i=0;\r
+ for(String s : str) {\r
+ if(first) {\r
+ if(++i>1) {\r
+ first = false;\r
+ pw().print("[");\r
+ }\r
+ } else {\r
+ pw().print("] [");\r
+ }\r
+ pw().print(s);\r
+ }\r
+ if(!first) {\r
+ pw().print(']');\r
+ }\r
+ pw().println();\r
+ reportLine();\r
+ }\r
+ \r
+ public String reportColHead(String format, String ... args) {\r
+ pw().format(format,(Object[])args);\r
+ reportLine();\r
+ return format;\r
+ }\r
+\r
+ public void reportLine() {\r
+ for(int i=0;i<lineLength;++i)pw().print('-');\r
+ pw().println();\r
+ }\r
+ \r
+ protected void setQueryParamsOn(Rcli<?> rcli) {\r
+ StringBuilder sb=null;\r
+ String force;\r
+ if((force=aafcli.forceString())!=null) {\r
+ sb = new StringBuilder("force=");\r
+ sb.append(force);\r
+ }\r
+ if(aafcli.addRequest()) {\r
+ if(sb==null) {\r
+ sb = new StringBuilder("request=true");\r
+ } else {\r
+ sb.append("&request=true");\r
+ }\r
+ }\r
+ if(sb!=null && rcli!=null) {\r
+ rcli.setQueryParams(sb.toString());\r
+ }\r
+ }\r
+//\r
+// /**\r
+// * If Force is set, will return True once only, then revert to "FALSE".\r
+// * \r
+// * @return\r
+// */\r
+// protected String checkForce() {\r
+// if(TRUE.equalsIgnoreCase(env.getProperty(FORCE, FALSE))) {\r
+// env.setProperty(FORCE, FALSE);\r
+// return "true";\r
+// }\r
+// return FALSE;\r
+// }\r
+\r
+ public String toString() {\r
+ StringBuilder sb = new StringBuilder();\r
+ if(parent==null) { // ultimate parent\r
+ build(sb,null);\r
+ return sb.toString();\r
+ } else {\r
+ return parent.toString();\r
+ }\r
+ }\r
+ \r
+ public String getOrgRealm() {\r
+ return env.getProperty(AAF_DEFAULT_REALM);\r
+ }\r
+}\r