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