Skip to content

Commit e73601e

Browse files
committed
refine CLI command return result presentation mechanism. How support TableView, JsonView and default ToString view
1 parent 6a9263b commit e73601e

18 files changed

Lines changed: 504 additions & 57 deletions

src/main/java/act/app/ActionContext.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -196,13 +196,19 @@ public String[] paramVals(String name) {
196196
return sa;
197197
}
198198

199-
public JSONObject jsonObject(String name) {
199+
public Object tryParseJson(String name, Class<?> paramType) {
200200
if (null == jsonObject) {
201201
return null;
202202
}
203203
Object o = jsonObject.get(name);
204-
if (null != o && o instanceof JSONObject) {
205-
return (JSONObject) o;
204+
if (null != o) {
205+
if (o instanceof JSONObject) {
206+
return JSON.parseObject(((JSONObject) o).toJSONString(), paramType);
207+
} else if (o instanceof JSONArray) {
208+
return JSON.parseArray(((JSONArray) o).toJSONString(), paramType);
209+
} else {
210+
return o;
211+
}
206212
} else {
207213
return jsonObject;
208214
}

src/main/java/act/cli/Command.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@
1616
*/
1717
String value();
1818

19+
/**
20+
* @return the help message for the command
21+
*/
22+
String help() default "";
23+
1924
public static class Util {
2025

2126
}

src/main/java/act/cli/HelpMsg.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@
1111
*/
1212
@Retention(RetentionPolicy.CLASS)
1313
@Target(ElementType.METHOD)
14+
@Deprecated
15+
/**
16+
* This is obsolete. Use {@link Command#help()} instead
17+
*/
1418
public @interface HelpMsg {
1519
/**
1620
* @return the help message
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package act.cli;
2+
3+
import java.lang.annotation.ElementType;
4+
import java.lang.annotation.Retention;
5+
import java.lang.annotation.RetentionPolicy;
6+
import java.lang.annotation.Target;
7+
8+
/**
9+
* Mark a command method return value shall be displayed as a JSON object. e.g
10+
* <pre>
11+
* [
12+
* {
13+
* "ID": "__act_app_stop",
14+
* "onetime": true,
15+
* "trigger": null
16+
* },
17+
* {
18+
* "id": "__act_app_app_act_plugin_loaded",
19+
* "onetime": true,
20+
* "trigger": null
21+
* }
22+
* ]
23+
* </pre>
24+
* <p>
25+
* {@code JsonView} can be used in conjunction with {@link act.util.PropertyFilter}
26+
* to export only specified fields
27+
* </p>
28+
* <p>
29+
* Note if a method is marked with neither {@link TableView} nor
30+
* {@link JsonView} then the console will simply use
31+
* {@link Object#toString()} to present the data
32+
* </p>
33+
* @see TableView
34+
* @see act.util.PropertyFilter
35+
*/
36+
@Retention(RetentionPolicy.CLASS)
37+
@Target(ElementType.METHOD)
38+
public @interface JsonView {
39+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package act.cli;
2+
3+
import java.lang.annotation.ElementType;
4+
import java.lang.annotation.Retention;
5+
import java.lang.annotation.RetentionPolicy;
6+
import java.lang.annotation.Target;
7+
8+
/**
9+
* Mark a command method return value shall be displayed as a table. e.g
10+
* <pre>
11+
* +---------------------------------+---------+---------+
12+
* | ID | ONETIME | TRIGGER |
13+
* +---------------------------------+---------+---------+
14+
* | __act_app_stop | true | null |
15+
* | __act_app_app_act_plugin_loaded | true | null |
16+
* +---------------------------------+---------+---------+
17+
* </pre>
18+
* <p>
19+
* {@code TableView} can be used in conjunction with {@link act.util.PropertyFilter}
20+
* to export only specified fields
21+
* </p>
22+
* <p>
23+
* Note if a method is marked with neither {@link TableView} nor
24+
* {@link JsonView} then the console will simply use
25+
* {@link Object#toString()} to present the data.
26+
* </p>
27+
* @see JsonView
28+
* @see act.util.PropertyFilter
29+
*/
30+
@Retention(RetentionPolicy.CLASS)
31+
@Target(ElementType.METHOD)
32+
public @interface TableView {
33+
}

src/main/java/act/cli/bytecode/CommanderByteCodeScanner.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import act.cli.meta.*;
88
import act.util.AsmTypes;
99
import act.util.ByteCodeVisitor;
10-
import act.util.DataView;
10+
import act.util.PropertyFilter;
1111
import org.osgl.$;
1212
import org.osgl.logging.L;
1313
import org.osgl.logging.Logger;
@@ -197,9 +197,17 @@ public void visit(String name, Object value) {
197197
throw E.unexpected("command name cannot be empty");
198198
}
199199
methodInfo.commandName(commandName);
200+
} else if (S.eq("help", name)) {
201+
methodInfo.helpMsg(S.string(value));
200202
}
201203
}
202204
};
205+
} else if ($.eq(AsmTypes.TABLE_VIEW.asmType(), type)) {
206+
methodInfo.view(CommandMethodMetaInfo.View.TABLE);
207+
return super.visitAnnotation(desc, visible);
208+
} else if ($.eq(AsmTypes.JSON_VIEW.asmType(), type)) {
209+
methodInfo.view(CommandMethodMetaInfo.View.JSON);
210+
return super.visitAnnotation(desc, visible);
203211
} else if ($.eq(AsmTypes.HELP_MSG.asmType(), type)) {
204212
return new AnnotationVisitor(ASM5, av) {
205213
@Override
@@ -210,7 +218,7 @@ public void visit(String name, Object value) {
210218
}
211219
};
212220
} else if ($.eq(AsmTypes.DATA_VIEW.asmType(), type)) {
213-
final DataView.MetaInfo info = new DataView.MetaInfo();
221+
final PropertyFilter.MetaInfo info = new PropertyFilter.MetaInfo();
214222
methodInfo.dataView(info);
215223
return new AnnotationVisitor(ASM5, av) {
216224
@Override

src/main/java/act/cli/meta/CommandMethodMetaInfo.java

Lines changed: 85 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,21 @@
11
package act.cli.meta;
22

33
import act.Destroyable;
4+
import act.app.CliContext;
45
import act.asm.Type;
6+
import act.cli.ascii_table.impl.CollectionASCIITableAware;
57
import act.sys.meta.InvokeType;
68
import act.sys.meta.ReturnTypeInfo;
7-
import act.util.DataView;
9+
import act.util.FastJsonPropertyPreFilter;
10+
import act.util.PropertyFilter;
811
import act.util.DestroyableBase;
12+
import com.alibaba.fastjson.serializer.SerializerFeature;
913
import org.osgl.$;
1014
import org.osgl.util.C;
1115
import org.osgl.util.E;
1216
import org.osgl.util.S;
1317

18+
import java.util.List;
1419
import java.util.Set;
1520

1621
/**
@@ -24,15 +29,82 @@
2429
*/
2530
public class CommandMethodMetaInfo extends DestroyableBase {
2631

32+
/**
33+
* Define how the command method return result should
34+
* be presented
35+
*/
36+
public static enum View {
37+
/**
38+
* present the result using {@link act.cli.TableView}
39+
*/
40+
TABLE () {
41+
@Override
42+
@SuppressWarnings("unchecked")
43+
public void print(Object result, PropertyFilter.MetaInfo filter, CliContext context) {
44+
if (null == filter) {
45+
// TODO: support Table View when filter annotation is not presented
46+
TO_STRING.print(result, null, context);
47+
return;
48+
}
49+
List dataList;
50+
if (result instanceof Iterable) {
51+
dataList = C.list((Iterable) result);
52+
} else {
53+
dataList = C.listOf(result);
54+
}
55+
context.printTable(new CollectionASCIITableAware(dataList, filter.outputFields(), filter.labels()));
56+
}
57+
},
58+
59+
/**
60+
* Present the result using {@link act.cli.JsonView}
61+
*/
62+
JSON () {
63+
@Override
64+
public void print(Object result, PropertyFilter.MetaInfo filter, CliContext context) {
65+
String json;
66+
if (null == filter) {
67+
json = com.alibaba.fastjson.JSON.toJSONString(result, SerializerFeature.PrettyFormat);
68+
} else {
69+
FastJsonPropertyPreFilter f = new FastJsonPropertyPreFilter();
70+
f.addIncludes(filter.outputFields());
71+
f.addExcludes(filter.excludedFields());
72+
json = com.alibaba.fastjson.JSON.toJSONString(result, f, SerializerFeature.PrettyFormat);
73+
}
74+
// TODO: handle labels in JSON serialization
75+
context.println(json);
76+
}
77+
},
78+
79+
/**
80+
* Present the result using {@link Object#toString()}
81+
*/
82+
TO_STRING () {
83+
@Override
84+
public void print(Object result, PropertyFilter.MetaInfo filter, CliContext context) {
85+
if (null != filter) {
86+
// if PropertyFilter annotation presented, then by default
87+
// use the JSON view to print the result
88+
JSON.print(result, filter, context);
89+
} else {
90+
context.println(result.toString());
91+
}
92+
}
93+
};
94+
95+
public abstract void print(Object result, PropertyFilter.MetaInfo filter, CliContext context);
96+
}
97+
2798
private String methodName;
2899
private String commandName;
29100
private String helpMsg;
30101
private InvokeType invokeType;
31102
private CommanderClassMetaInfo clsInfo;
32-
private DataView.MetaInfo dataView;
103+
private PropertyFilter.MetaInfo dataView;
33104
private C.List<CommandParamMetaInfo> params = C.newList();
34105
private ReturnTypeInfo returnType;
35106
private Set<String> optionLeads = C.newSet();
107+
private View view = View.TO_STRING;
36108

37109
public CommandMethodMetaInfo(CommanderClassMetaInfo clsInfo) {
38110
this.clsInfo = $.NPE(clsInfo);
@@ -51,6 +123,15 @@ public String methodName() {
51123
return methodName;
52124
}
53125

126+
public CommandMethodMetaInfo view(View view) {
127+
this.view = $.notNull(view);
128+
return this;
129+
}
130+
131+
public View view() {
132+
return view;
133+
}
134+
54135
public CommandMethodMetaInfo commandName(String name) {
55136
commandName = $.NPE(name);
56137
return this;
@@ -87,12 +168,12 @@ public boolean isStatic() {
87168
return InvokeType.STATIC == invokeType;
88169
}
89170

90-
public CommandMethodMetaInfo dataView(DataView.MetaInfo dataView) {
171+
public CommandMethodMetaInfo dataView(PropertyFilter.MetaInfo dataView) {
91172
this.dataView = dataView;
92173
return this;
93174
}
94175

95-
public DataView.MetaInfo dataViewInfo() {
176+
public PropertyFilter.MetaInfo dataViewInfo() {
96177
return dataView;
97178
}
98179

src/main/java/act/handler/builtin/cli/CliHandlerProxy.java

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import act.cli.bytecode.ReflectedCommandExecutor;
99
import act.cli.meta.CommandMethodMetaInfo;
1010
import act.handler.CliHandlerBase;
11-
import act.util.DataView;
11+
import act.util.PropertyFilter;
1212
import org.osgl.$;
1313
import org.osgl.logging.L;
1414
import org.osgl.logging.Logger;
@@ -63,18 +63,8 @@ private void onResult(Object result, CliContext context) {
6363
if (null == result) {
6464
return;
6565
}
66-
DataView.MetaInfo dataView = meta.dataViewInfo();
67-
if (null != dataView) {
68-
List dataList;
69-
if (result instanceof Iterable) {
70-
dataList = C.list((Iterable) result);
71-
} else {
72-
dataList = C.listOf(result);
73-
}
74-
context.printTable(new CollectionASCIITableAware(dataList, dataView.outputFields(), dataView.labels()));
75-
} else {
76-
context.println(result.toString());
77-
}
66+
PropertyFilter.MetaInfo filter = meta.dataViewInfo();
67+
meta.view().print(result, filter, context);
7868
}
7969

8070
private void ensureAgentsReady() {

src/main/java/act/handler/builtin/controller/impl/ReflectedHandlerInvoker.java

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -266,19 +266,12 @@ private Object[] params(ActionContext ctx, Result result, Exception exception) {
266266
}
267267
String reqVal = ctx.paramVal(bindName);
268268
if (null == reqVal) {
269-
JSONObject jsonObject = ctx.jsonObject(bindName);
270-
if (null != jsonObject) {
271-
// todo: implement a more efficient way to deserialize JSON
272-
Object o = JSON.parseObject(jsonObject.toJSONString(), paramType);
269+
Object o = ctx.tryParseJson(bindName, paramType);
270+
if (null != o) {
273271
oa[i] = o;
274272
continue;
275-
} else {
276-
JSONArray jsonArray = ctx.jsonArray();
277-
if (null != jsonArray) {
278-
// todo: map JSONObject to object type
279-
}
280273
}
281-
Object o = param.defVal(paramType);
274+
o = param.defVal(paramType);
282275
if (null != o) {
283276
oa[i] = o;
284277
continue;

src/main/java/act/job/JobAdmin.java

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
11
package act.job;
22

33
import act.app.App;
4-
import act.cli.Command;
5-
import act.cli.HelpMsg;
6-
import act.cli.Optional;
7-
import act.cli.Required;
8-
import act.util.DataView;
4+
import act.cli.*;
5+
import act.util.PropertyFilter;
96
import com.alibaba.fastjson.JSONObject;
107
import org.osgl.$;
118
import org.osgl.util.C;
@@ -23,9 +20,9 @@ public class JobAdmin {
2320
* List all jobs in the job manager
2421
* @return a list of {@link _Job jobs}
2522
*/
26-
@Command("act.job.list")
27-
@HelpMsg("List jobs")
28-
@DataView(_Job.LIST_VIEW)
23+
@Command(value = "act.job.list", help = "List jobs")
24+
@PropertyFilter(_Job.BRIEF_VIEW)
25+
@TableView
2926
public List<_Job> listJobs(@Optional("-q") final String q, App app) {
3027
AppJobManager jobManager = app.jobManager();
3128
C.List<_Job> jobs = jobManager.jobs();
@@ -40,15 +37,15 @@ public boolean test(_Job job) {
4037
return jobs;
4138
}
4239

43-
@Command("act.job.show")
44-
@HelpMsg("Show job")
40+
@Command(value = "act.job.show", help = "Show job details")
41+
@JsonView
42+
@PropertyFilter(_Job.DETAIL_VIEW)
4543
public _Job getJob(@Required(value = "-i,--id", help = "specify the query string") final String id, App app) {
4644
AppJobManager jobManager = app.jobManager();
4745
return jobManager.jobById(id);
4846
}
4947

50-
@Command("act.job.scheduler")
51-
@HelpMsg("Show Job manager scheduler status")
48+
@Command(value = "act.job.scheduler", help = "Show Job manager scheduler status")
5249
public String getSchedulerStatus(App app) {
5350
AppJobManager jobManager = app.jobManager();
5451
ScheduledThreadPoolExecutor executor = jobManager.executor();

0 commit comments

Comments
 (0)