0439eb86bf06fa4140441cae0b18b52205f64254
[aaf/authz.git] / authz-cmd / src / main / java / com / att / cmd / Cmd.java
1 /*******************************************************************************\r
2  * ============LICENSE_START====================================================\r
3  * * org.onap.aai\r
4  * * ===========================================================================\r
5  * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.\r
6  * * Copyright © 2017 Amdocs\r
7  * * ===========================================================================\r
8  * * Licensed under the Apache License, Version 2.0 (the "License");\r
9  * * you may not use this file except in compliance with the License.\r
10  * * You may obtain a copy of the License at\r
11  * * \r
12  *  *      http://www.apache.org/licenses/LICENSE-2.0\r
13  * * \r
14  *  * Unless required by applicable law or agreed to in writing, software\r
15  * * distributed under the License is distributed on an "AS IS" BASIS,\r
16  * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
17  * * See the License for the specific language governing permissions and\r
18  * * limitations under the License.\r
19  * * ============LICENSE_END====================================================\r
20  * *\r
21  * * ECOMP is a trademark and service mark of AT&T Intellectual Property.\r
22  * *\r
23  ******************************************************************************/\r
24 package com.att.cmd;\r
25 \r
26 import java.io.PrintWriter;\r
27 import java.io.StringReader;\r
28 import java.sql.Date;\r
29 import java.text.DateFormat;\r
30 import java.text.SimpleDateFormat;\r
31 import java.util.ArrayList;\r
32 import java.util.Comparator;\r
33 import java.util.GregorianCalendar;\r
34 import java.util.List;\r
35 import java.util.Stack;\r
36 import java.util.concurrent.ConcurrentHashMap;\r
37 \r
38 import com.att.cadi.CadiException;\r
39 import com.att.cadi.LocatorException;\r
40 import com.att.cadi.client.Future;\r
41 import com.att.cadi.client.Rcli;\r
42 import com.att.cadi.client.Retryable;\r
43 import com.att.cadi.http.HMangr;\r
44 import com.att.cssa.rserv.HttpMethods;\r
45 import com.att.inno.env.APIException;\r
46 import com.att.inno.env.Data.TYPE;\r
47 import com.att.inno.env.Env;\r
48 import com.att.inno.env.util.Chrono;\r
49 import com.att.rosetta.env.RosettaDF;\r
50 import com.att.rosetta.env.RosettaEnv;\r
51 \r
52 import aaf.v2_0.Error;\r
53 import aaf.v2_0.History;\r
54 import aaf.v2_0.History.Item;\r
55 import aaf.v2_0.Request;\r
56 \r
57 \r
58 public abstract class Cmd {\r
59         private static final String AAF_DEFAULT_REALM = "aaf_default_realm";\r
60         \r
61         private static final DateFormat dateFmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");\r
62         protected static final String BLANK = "";\r
63         protected static final String COMMA = ","; // for use in splits\r
64 \r
65         protected static final int lineLength = 80;\r
66 \r
67         private final static String hformat = "%-23s %-5s %-20s %-35s\n";\r
68 \r
69         public static final String STARTDATE = "startdate";\r
70         public static final String ENDDATE = "enddate";\r
71         \r
72         private String name;\r
73         private final Param[] params;\r
74         private int required;\r
75         protected final Cmd parent;\r
76         protected final List<Cmd> children;\r
77         private final ConcurrentHashMap<Class<?>,RosettaDF<?>> dfs = new ConcurrentHashMap<Class<?>,RosettaDF<?>>();\r
78         public final AAFcli aafcli;\r
79         protected Env env;\r
80 \r
81         public Cmd(AAFcli aafcli, String name, Param ... params) {\r
82                 this(aafcli,null, name,params);\r
83         }\r
84 \r
85         public Cmd(Cmd parent, String name, Param ... params) {\r
86                 this(parent.aafcli,parent, name,params);\r
87         }\r
88 \r
89         Cmd(AAFcli aafcli, Cmd parent, String name, Param ... params) {\r
90                 this.parent = parent;\r
91                 this.aafcli = aafcli;\r
92                 this.env = aafcli.env;\r
93                 if(parent!=null) {\r
94                         parent.children.add(this);\r
95                 }\r
96                 children = new ArrayList<Cmd>();\r
97                 this.params = params;\r
98                 this.name = name;\r
99                 required=0;\r
100                 for(Param p : params) {\r
101                         if(p.required) {\r
102                                 ++required;\r
103                         }\r
104                 }\r
105         }\r
106         \r
107         public final int exec(int idx, String ... args) throws CadiException, APIException, LocatorException {\r
108                 if(args.length-idx<required) {\r
109                         throw new CadiException(build(new StringBuilder("Too few args: "),null).toString());\r
110                 }\r
111                 return _exec(idx,args);\r
112         }\r
113         \r
114         protected abstract int _exec(int idx, final String ... args) throws CadiException, APIException, LocatorException;\r
115         \r
116         public void detailedHelp(int indent,StringBuilder sb) {\r
117         }\r
118 \r
119         protected void detailLine(StringBuilder sb, int length, String s) {\r
120                 multiChar(sb,length,' ',0);\r
121                 sb.append(s);\r
122         }\r
123 \r
124         public void apis(int indent,StringBuilder sb) {\r
125         }\r
126 \r
127         protected void api(StringBuilder sb, int _indent, HttpMethods hmeth, String pathInfo, Class<?> cls,boolean head) {\r
128             int indent = _indent;\r
129             final String meth = hmeth.name();\r
130                 if(head) {\r
131                         sb.append('\n');\r
132                         detailLine(sb,indent,"APIs:");\r
133                 }\r
134                 indent+=2;\r
135                 multiChar(sb,indent,' ',0);\r
136                 sb.append(meth);\r
137                 sb.append(' ');\r
138                 sb.append(pathInfo);\r
139                 String cliString = aafcli.typeString(cls,true);\r
140                 if(indent+meth.length()+pathInfo.length()+cliString.length()+2>80) {\r
141                         sb.append(" ...");\r
142                         multiChar(sb,indent+3+meth.length(),' ',0);\r
143                 } else { // same line\r
144                         sb.append(' ');\r
145                 }\r
146                 sb.append(cliString);\r
147         }\r
148 \r
149         protected void multiChar(StringBuilder sb, int length, char c, int indent) {\r
150                 sb.append('\n');\r
151                 for(int i=0;i<indent;++i)sb.append(' ');\r
152                 for(int i=indent;i<length;++i)sb.append(c);\r
153         }\r
154 \r
155         public StringBuilder build(StringBuilder sb, StringBuilder detail) {\r
156                 if(name!=null) {\r
157                         sb.append(name);\r
158                         sb.append(' ');\r
159                 }\r
160                 int line = sb.lastIndexOf("\n")+1;\r
161                 if(line<0) {\r
162                         line=0;\r
163                 }\r
164                 int indent = sb.length()-line;\r
165                 for(Param p : params) {\r
166                         sb.append(p.required?'<':'[');\r
167                         sb.append(p.tag);\r
168                         sb.append(p.required?"> ": "] ");\r
169                 }\r
170                 \r
171                 boolean first = true;\r
172                 for(Cmd child : children) {\r
173                         if(first) {\r
174                                 first = false;\r
175                         } else if(detail==null) {\r
176                                 multiChar(sb,indent,' ',0);\r
177                         } else {\r
178                                 // Write parents for Detailed Report\r
179                                 Stack<String> stack = new Stack<String>();\r
180                                 for(Cmd c = child.parent;c!=null;c=c.parent) {\r
181                                         if(c.name!=null) {\r
182                                                 stack.push(c.name);\r
183                                         }\r
184                                 }\r
185                                 if(!stack.isEmpty()) {\r
186                                         sb.append("  ");\r
187                                         while(!stack.isEmpty()) {\r
188                                                 sb.append(stack.pop());\r
189                                                 sb.append(' ');\r
190                                         }\r
191                                 }\r
192                         }\r
193                         child.build(sb,detail);\r
194                         if(detail!=null) {\r
195                                 child.detailedHelp(4, detail);\r
196                                 // If Child wrote something, then add, bracketing by lines\r
197                                 if(detail.length()>0) {\r
198                                         multiChar(sb,80,'-',2);\r
199                                         sb.append(detail);\r
200                                         sb.append('\n');\r
201                                         multiChar(sb,80,'-',2);\r
202                                         sb.append('\n');\r
203                                         detail.setLength(0); // reuse\r
204                                 } else {\r
205                                         sb.append('\n');\r
206                                 }\r
207                         }\r
208                 }\r
209                 return sb;\r
210         }\r
211         \r
212         protected void error(Future<?> future) {\r
213                 StringBuilder sb = new StringBuilder("Failed");\r
214                 String desc = future.body();\r
215                 int code = future.code();\r
216                 if(desc==null || desc.length()==0) {\r
217                         withCode(sb,code);\r
218                 } else if(desc.startsWith("{")) {\r
219                         StringReader sr = new StringReader(desc);\r
220                         try {\r
221                                 // Note: 11-18-2013.  This rather convoluted Message Structure required by TSS Restful Specs, reflecting "Northbound" practices.\r
222                                 Error err = getDF(Error.class).newData().in(TYPE.JSON).load(sr).asObject();\r
223                                 sb.append(" [");\r
224                                 sb.append(err.getMessageId());\r
225                                 sb.append("]: ");\r
226                                 String messageBody = err.getText();\r
227                                 List<String> vars = err.getVariables();\r
228                                 int pipe;\r
229                                 for (int varCounter=0;varCounter<vars.size();) {\r
230                                         String var = vars.get(varCounter);\r
231                                         ++varCounter;\r
232                                         if (messageBody.indexOf("%" + varCounter) >= 0) {\r
233                                                 if((pipe = var.indexOf('|'))>=0) {  // In AAF, we use a PIPE for Choice\r
234                                                         if (aafcli.isTest()) {\r
235                                                                 String expiresStr = var.substring(pipe);\r
236                                                                 var = var.replace(expiresStr, "[Placeholder]");\r
237                                                         } else {\r
238                                                                 StringBuilder varsb = new StringBuilder(var);\r
239                                                                 varsb.deleteCharAt(pipe);\r
240                                                                 var = varsb.toString();\r
241                                                         }\r
242                                                         messageBody = messageBody.replace("%" + varCounter, varCounter-1 + ") " + var);\r
243                                                 } else {\r
244                                                         messageBody = messageBody.replace("%" + varCounter, var);\r
245                                                 }\r
246                                         }\r
247                                 }\r
248                                 sb.append(messageBody);\r
249                         } catch (Exception e) {\r
250                                 withCode(sb,code);\r
251                                 sb.append(" (Note: Details cannot be obtained from Error Structure)");\r
252                         }\r
253                 } else if(desc.startsWith("<html>")){ // Core Jetty, etc sends HTML for Browsers\r
254                         withCode(sb,code);\r
255                 } else {\r
256                         sb.append(" with code ");\r
257                         sb.append(code);\r
258                         sb.append(", ");\r
259                         sb.append(desc);\r
260                 }\r
261                 pw().println(sb);\r
262         }\r
263 \r
264         \r
265         private void withCode(StringBuilder sb, Integer code) {\r
266                 sb.append(" with code ");\r
267                 sb.append(code);\r
268                 switch(code) {\r
269                         case 401:\r
270                                 sb.append(" (HTTP Not Authenticated)");\r
271                                 break;\r
272                         case 403:\r
273                                 sb.append(" (HTTP Forbidden)");\r
274                                 break;\r
275                         case 404:\r
276                                 sb.append(" (HTTP Not Found)");\r
277                                 break;\r
278                         default:\r
279                 }\r
280         }\r
281 \r
282         /**\r
283          * Consistently set start and end dates from Requests (all derived from Request)\r
284          * @param req\r
285          */\r
286         protected void setStartEnd(Request req) {\r
287                 // Set Start/End Dates, if exist\r
288                 String str;\r
289                 if((str = env.getProperty(Cmd.STARTDATE,null))!=null) {\r
290                         req.setStart(Chrono.timeStamp(Date.valueOf(str)));\r
291                 }\r
292                 \r
293                 if((str = env.getProperty(Cmd.ENDDATE,null))!=null) {\r
294                         req.setEnd(Chrono.timeStamp(Date.valueOf(str)));\r
295                 }\r
296         }\r
297 \r
298         @SuppressWarnings("unchecked")\r
299         protected<T> RosettaDF<T> getDF(Class<T> cls) throws APIException {\r
300                 RosettaDF<T> rdf = (RosettaDF<T>)dfs.get(cls);\r
301                 if(rdf == null) {\r
302                         rdf = env().newDataFactory(cls);\r
303                         dfs.put(cls, rdf);\r
304                 }\r
305                 return rdf;\r
306         }\r
307 \r
308         public void activity(History history, String header) {\r
309                 if (history.getItem().isEmpty()) {\r
310                         int start = header.indexOf('[');\r
311                         if (start >= 0) {\r
312                                 pw().println("No Activity Found for " + header.substring(start));\r
313                         }\r
314                 } else {\r
315                         pw().println(header);\r
316                         for(int i=0;i<lineLength;++i)pw().print('-');\r
317                         pw().println();\r
318                                                                 \r
319                         pw().format(hformat,"Date","Table","User","Memo");\r
320                         for(int i=0;i<lineLength;++i)pw().print('-');\r
321                         pw().println();\r
322         \r
323                         // Save Server time by Sorting locally\r
324                         List<Item> items = history.getItem();\r
325                         java.util.Collections.sort(items, new Comparator<Item>() {\r
326                                 @Override\r
327                                 public int compare(Item o1, Item o2) {\r
328                                         return o2.getTimestamp().compare(o1.getTimestamp());\r
329                                 }\r
330                         });\r
331                         \r
332                         for(History.Item item : items) {\r
333                                 GregorianCalendar gc = item.getTimestamp().toGregorianCalendar();\r
334                                 pw().format(hformat,\r
335                                         dateFmt.format(gc.getTime()),\r
336                                         item.getTarget(),\r
337                                         item.getUser(),\r
338                                         item.getMemo());\r
339                         }\r
340                 }\r
341         }\r
342         \r
343         /**\r
344          * Turn String Array into a | delimited String\r
345          * @param options\r
346          * @return\r
347          */\r
348         public static String optionsToString(String[] options) {\r
349                 StringBuilder sb = new StringBuilder();\r
350                 boolean first = true;\r
351                 for(String s : options) {\r
352                         if(first) {\r
353                                 first = false;\r
354                         } else {\r
355                                 sb.append('|');\r
356                         }\r
357                         sb.append(s);\r
358                 }\r
359                 return sb.toString();\r
360         }\r
361         \r
362         /**\r
363          * return which index number the Option matches.\r
364          * \r
365          * throws an Exception if not part of this Option Set\r
366          * \r
367          * @param options\r
368          * @param test\r
369          * @return\r
370          * @throws Exception\r
371          */\r
372         public int whichOption(String[] options, String test) throws CadiException {\r
373                 for(int i=0;i<options.length;++i) {\r
374                         if(options[i].equals(test)) {\r
375                                 return i;\r
376                         }\r
377                 }\r
378                 throw new CadiException(build(new StringBuilder("Invalid Option: "),null).toString());\r
379         }\r
380 \r
381         protected RosettaEnv env() {\r
382                 return aafcli.env;\r
383         }\r
384 \r
385         protected HMangr hman() {\r
386                 return aafcli.hman;\r
387         }\r
388 \r
389         public<RET> RET same(Retryable<RET> retryable) throws APIException, CadiException, LocatorException {\r
390                 // We're storing in AAFCli, because we know it's always the same, and single threaded\r
391                 if(aafcli.prevCall!=null) {\r
392                         retryable.item(aafcli.prevCall.item());\r
393                         retryable.lastClient=aafcli.prevCall.lastClient;\r
394                 }\r
395                 \r
396                 RET ret = aafcli.hman.same(aafcli.ss,retryable);\r
397                 \r
398                 // Store last call in AAFcli, because Cmds are all different instances.\r
399                 aafcli.prevCall = retryable;\r
400                 return ret;\r
401         }\r
402 \r
403         public<RET> RET all(Retryable<RET> retryable) throws APIException, CadiException, LocatorException {\r
404                 this.setQueryParamsOn(retryable.lastClient);\r
405                 return aafcli.hman.all(aafcli.ss,retryable);\r
406         }\r
407 \r
408         public<RET> RET oneOf(Retryable<RET> retryable,String host) throws APIException, CadiException, LocatorException {\r
409                 this.setQueryParamsOn(retryable.lastClient);\r
410                 return aafcli.hman.oneOf(aafcli.ss,retryable,true,host);\r
411         }\r
412 \r
413         protected PrintWriter pw() {\r
414                 return AAFcli.pw;\r
415         }\r
416 \r
417         public String getName() {\r
418                 return name;\r
419         }\r
420         \r
421         public void reportHead(String ... str) {\r
422                 pw().println();\r
423                 boolean first = true;\r
424                 int i=0;\r
425                 for(String s : str) {\r
426                         if(first) {\r
427                                 if(++i>1) {\r
428                                         first = false;\r
429                                         pw().print("[");\r
430                                 }\r
431                         } else {\r
432                                 pw().print("] [");\r
433                         }\r
434                         pw().print(s);\r
435                 }\r
436                 if(!first) {\r
437                         pw().print(']');\r
438                 }\r
439                 pw().println();\r
440                 reportLine();\r
441         }\r
442         \r
443         public String reportColHead(String format, String ...  args) {\r
444                 pw().format(format,(Object[])args);\r
445                 reportLine();\r
446                 return format;\r
447         }\r
448 \r
449         public void reportLine() {\r
450                 for(int i=0;i<lineLength;++i)pw().print('-');\r
451                 pw().println();\r
452         }\r
453         \r
454         protected void setQueryParamsOn(Rcli<?> rcli) {\r
455                 StringBuilder sb=null;\r
456                 String force;\r
457                 if((force=aafcli.forceString())!=null) {\r
458                         sb = new StringBuilder("force=");\r
459                         sb.append(force);\r
460                 }\r
461                 if(aafcli.addRequest()) {\r
462                         if(sb==null) {\r
463                                 sb = new StringBuilder("request=true");\r
464                         } else {\r
465                                 sb.append("&request=true");\r
466                         }\r
467                 }\r
468                 if(sb!=null && rcli!=null) {\r
469                         rcli.setQueryParams(sb.toString());\r
470                 }\r
471         }\r
472 //\r
473 //      /**\r
474 //       * If Force is set, will return True once only, then revert to "FALSE".\r
475 //       *  \r
476 //       * @return\r
477 //       */\r
478 //      protected String checkForce() {\r
479 //              if(TRUE.equalsIgnoreCase(env.getProperty(FORCE, FALSE))) {\r
480 //                      env.setProperty(FORCE, FALSE);\r
481 //                      return "true";\r
482 //              }\r
483 //              return FALSE;\r
484 //      }\r
485 \r
486         public String toString() {\r
487                 StringBuilder sb = new StringBuilder();\r
488                 if(parent==null) { // ultimate parent\r
489                         build(sb,null);\r
490                         return sb.toString();\r
491                 } else {\r
492                         return parent.toString();\r
493                 }\r
494         }\r
495         \r
496         public String getOrgRealm() {\r
497                 return env.getProperty(AAF_DEFAULT_REALM);\r
498         }\r
499 }\r