Skip to content

Commit 5c483ee

Browse files
committed
actframework#539 Implement new response output model
1 parent 567a845 commit 5c483ee

15 files changed

Lines changed: 547 additions & 246 deletions

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
**1.7.4**
44
* notFoundIfNull return Content-Type as text/html #537
5+
* Implement new response output model #539
56

67
**1.7.3**
78
* Fix regression issue with #504

pom.xml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,11 +62,11 @@
6262
<progressbar.version>0.5.5</progressbar.version>
6363
<joda-time.version>2.9.9</joda-time.version>
6464
<okhttp.version>3.9.1</okhttp.version>
65-
<osgl-tool.version>1.6.2</osgl-tool.version>
65+
<osgl-tool.version>1.7.0-SNAPSHOT</osgl-tool.version>
6666
<osgl-cache.version>1.2.0</osgl-cache.version>
6767
<osgl-genie.version>1.4.0</osgl-genie.version>
68-
<osgl-http.version>1.3.0</osgl-http.version>
69-
<osgl-mvc.version>1.4.0</osgl-mvc.version>
68+
<osgl-http.version>1.4.0-SNAPSHOT</osgl-http.version>
69+
<osgl-mvc.version>1.5.0-SNAPSHOT</osgl-mvc.version>
7070
<osgl-storage.version>1.5.0</osgl-storage.version>
7171
<osgl-tool-ext.version>1.1.0</osgl-tool-ext.version>
7272
<osgl-ut.version>2.0.0-BETA-1</osgl-ut.version>

src/main/java/act/ActResponse.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,11 @@ public void commitContentType() {
9797
header(H.Header.Names.CONTENT_TYPE, _getContentType());
9898
}
9999

100-
public abstract void closeStreamAndWriter();
100+
@Override
101+
public void close() {
102+
super.close();
103+
markClosed();
104+
}
101105

102106
public boolean isClosed() {
103107
return closed;
@@ -122,7 +126,7 @@ public int statusCode() {
122126
return statusCode;
123127
}
124128

125-
protected void beforeWritingContent() {
129+
public void beforeWritingContent() {
126130
if (ready) {
127131
return;
128132
}
@@ -131,7 +135,7 @@ protected void beforeWritingContent() {
131135
MvcConfig.applyBeforeCommitResultHandler(Ok.get(), ctx.req(), this);
132136
}
133137

134-
protected void afterWritingContent() {
138+
public void afterWritingContent() {
135139
if (ready) {
136140
return;
137141
}

src/main/java/act/cli/view/CliView.java

Lines changed: 90 additions & 131 deletions
Large diffs are not rendered by default.

src/main/java/act/controller/Controller.java

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,11 @@
3535
import org.osgl.http.H;
3636
import org.osgl.mvc.result.*;
3737
import org.osgl.storage.ISObject;
38-
import org.osgl.util.C;
39-
import org.osgl.util.E;
40-
import org.osgl.util.IO;
41-
import org.osgl.util.S;
38+
import org.osgl.util.*;
4239

4340
import javax.inject.Inject;
4441
import java.io.File;
4542
import java.io.InputStream;
46-
import java.io.OutputStream;
4743
import java.lang.annotation.ElementType;
4844
import java.lang.annotation.Retention;
4945
import java.lang.annotation.RetentionPolicy;
@@ -1123,16 +1119,16 @@ public static RenderBinary renderBinary(File file) {
11231119
* @param outputStreamWriter the delayed writer
11241120
* @return the result
11251121
*/
1126-
public static RenderBinary binary($.Function<OutputStream, ?> outputStreamWriter) {
1122+
public static RenderBinary binary($.Visitor<Output> outputStreamWriter) {
11271123
return new RenderBinary(outputStreamWriter);
11281124
}
11291125

11301126
/**
1131-
* Alias of {@link #binary(Osgl.Function)}
1127+
* Alias of {@link #binary(Osgl.Visitor)}
11321128
* @param outputStreamWriter the delayed writer
11331129
* @return the result
11341130
*/
1135-
public static RenderBinary renderBinary($.Function<OutputStream, ?> outputStreamWriter) {
1131+
public static RenderBinary renderBinary($.Visitor<Output> outputStreamWriter) {
11361132
return binary(outputStreamWriter);
11371133
}
11381134

@@ -1467,9 +1463,6 @@ public static Result inferResult(HandlerMethodMetaInfo meta, Object v, ActionCon
14671463
}
14681464
PropertySpec.MetaInfo propertySpec = PropertySpec.MetaInfo.withCurrent(meta, context);
14691465
try {
1470-
if (null == propertySpec) {
1471-
return RenderJSON.of(status, v);
1472-
}
14731466
return FilteredRenderJSON.get(status, v, propertySpec, context);
14741467
} finally {
14751468
if (meta.disableJsonCircularRefDetect()) {

src/main/java/act/controller/ResponseCache.java

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import org.osgl.storage.ISObject;
2828
import org.osgl.util.Charsets;
2929
import org.osgl.util.IO;
30+
import org.osgl.util.Output;
3031

3132
import java.io.OutputStream;
3233
import java.io.PrintWriter;
@@ -248,6 +249,11 @@ protected Class _impl() {
248249
return getClass();
249250
}
250251

252+
@Override
253+
protected Output createOutput() {
254+
return null;
255+
}
256+
251257
@Override
252258
protected OutputStream createOutputStream() {
253259
return null;
@@ -347,13 +353,4 @@ public void commit() {
347353
realResponse.commit();
348354
}
349355

350-
@Override
351-
public void closeStreamAndWriter() {
352-
if (null != writerCache) {
353-
IO.close(writerCache);
354-
} else if (null != osCache) {
355-
IO.close(osCache);
356-
}
357-
realResponse.closeStreamAndWriter();
358-
}
359356
}

src/main/java/act/util/JsonUtilConfig.java

Lines changed: 181 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@
99
* Licensed under the Apache License, Version 2.0 (the "License");
1010
* you may not use this file except in compliance with the License.
1111
* You may obtain a copy of the License at
12-
*
12+
*
1313
* http://www.apache.org/licenses/LICENSE-2.0
14-
*
14+
*
1515
* Unless required by applicable law or agreed to in writing, software
1616
* distributed under the License is distributed on an "AS IS" BASIS,
1717
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -21,11 +21,11 @@
2121
*/
2222

2323
import act.app.App;
24+
import act.cli.util.MappedFastJsonNameFilter;
25+
import act.data.DataPropertyRepository;
2426
import com.alibaba.fastjson.JSON;
2527
import com.alibaba.fastjson.parser.ParserConfig;
26-
import com.alibaba.fastjson.serializer.SerializeConfig;
27-
import com.alibaba.fastjson.serializer.SerializeFilter;
28-
import com.alibaba.fastjson.serializer.SerializerFeature;
28+
import com.alibaba.fastjson.serializer.*;
2929
import org.joda.time.DateTime;
3030
import org.joda.time.LocalDate;
3131
import org.joda.time.LocalDateTime;
@@ -38,10 +38,152 @@
3838
import org.osgl.storage.impl.SObject;
3939
import org.osgl.util.*;
4040

41+
import java.io.IOException;
42+
import java.io.OutputStream;
43+
import java.io.Writer;
44+
import java.nio.charset.StandardCharsets;
45+
import java.util.HashSet;
46+
import java.util.LinkedHashSet;
47+
import java.util.List;
48+
import java.util.Set;
49+
4150
import static com.alibaba.fastjson.JSON.DEFAULT_GENERATE_FEATURE;
4251

4352
public class JsonUtilConfig {
44-
public static void configure(App app) {
53+
54+
public static class JsonWriter extends $.Visitor<org.osgl.util.Output> {
55+
56+
private final Object v;
57+
private SerializerFeature[] features;
58+
private SerializeFilter[] filters;
59+
private String dateFormatPattern;
60+
private boolean hasPropFilter;
61+
62+
public JsonWriter(Object v, PropertySpec.MetaInfo spec, boolean format, ActContext context) {
63+
if (null == v) {
64+
this.v = "{}";
65+
} else if (v instanceof String) {
66+
String s = S.string(v).trim();
67+
int len = s.length();
68+
if (0 == len) {
69+
this.v = "{}";
70+
} else {
71+
char a = s.charAt(0);
72+
char z = s.charAt(len - 1);
73+
if (('{' == a && '}' == z) || ('[' == a && ']' == z)) {
74+
this.v = s;
75+
} else {
76+
this.v = "{\"result\":" + s + "}";
77+
}
78+
}
79+
} else {
80+
this.v = v;
81+
this.dateFormatPattern = null == context ? null : context.dateFormatPattern();
82+
this.filters = initFilters(v, spec, context);
83+
this.features = initFeatures(format, context);
84+
}
85+
}
86+
87+
private SerializeFilter[] initFilters(Object v, PropertySpec.MetaInfo spec, ActContext context) {
88+
Set<SerializeFilter> filterSet = new LinkedHashSet<>();
89+
FastJsonPropertyPreFilter propertyFilter = initPropertyPreFilter(v, spec, context);
90+
if (null != propertyFilter) {
91+
hasPropFilter = true;
92+
}
93+
if (null != spec && null != context) {
94+
MappedFastJsonNameFilter nameFilter = new MappedFastJsonNameFilter(spec.labelMapping(context));
95+
filterSet.add(nameFilter);
96+
}
97+
98+
if (null != context) {
99+
SerializeFilter[] filters = context.fastjsonFilters();
100+
if (null != filters) {
101+
for (SerializeFilter f : filters) {
102+
filterSet.add(f);
103+
}
104+
}
105+
}
106+
if (null != propertyFilter) {
107+
filterSet.add(propertyFilter);
108+
}
109+
return filterSet.toArray(new SerializeFilter[filterSet.size()]);
110+
}
111+
112+
private SerializerFeature[] initFeatures(boolean format, ActContext context) {
113+
Set<SerializerFeature> featureSet = new HashSet<>();
114+
if (format) {
115+
featureSet.add(SerializerFeature.PrettyFormat);
116+
}
117+
if (null != context) {
118+
SerializerFeature[] features = context.fastjsonFeatures();
119+
if (null != features) {
120+
for (SerializerFeature f : features) {
121+
featureSet.add(f);
122+
}
123+
}
124+
}
125+
Boolean b = DisableFastJsonCircularReferenceDetect.option.get();
126+
if (null != b && b) {
127+
featureSet.add(SerializerFeature.DisableCircularReferenceDetect);
128+
}
129+
featureSet.add(SerializerFeature.WriteDateUseDateFormat);
130+
return featureSet.toArray(new SerializerFeature[featureSet.size()]);
131+
}
132+
133+
private FastJsonPropertyPreFilter initPropertyPreFilter(Object v, PropertySpec.MetaInfo spec, ActContext context) {
134+
if (null != context) {
135+
spec = PropertySpec.MetaInfo.withCurrent(spec, context);
136+
}
137+
if (null == spec) {
138+
return null;
139+
}
140+
FastJsonPropertyPreFilter propertyFilter = new FastJsonPropertyPreFilter();
141+
List<String> outputs = spec.outputFields(context);
142+
Set<String> excluded = spec.excludedFields(context);
143+
if (excluded.isEmpty()) {
144+
if (outputs.isEmpty()) {
145+
propertyFilter = null; // no filter defined actually
146+
} else {
147+
// output fields only applied when excluded fields not presented
148+
propertyFilter.addIncludes(outputs);
149+
if (FastJsonPropertyPreFilter.hasPattern(outputs)) {
150+
// TODO: handle the case when result is an Iterable
151+
propertyFilter.setFullPaths(context.app().service(DataPropertyRepository.class).propertyListOf(v.getClass()));
152+
}
153+
}
154+
} else {
155+
propertyFilter.addExcludes(excluded);
156+
if (FastJsonPropertyPreFilter.hasPattern(excluded)) {
157+
// TODO: handle the case when result is an Iterable
158+
propertyFilter.setFullPaths(context.app().service(DataPropertyRepository.class).propertyListOf(v.getClass()));
159+
}
160+
}
161+
return propertyFilter;
162+
}
163+
164+
@Override
165+
public void visit(org.osgl.util.Output output) throws Osgl.Break {
166+
try {
167+
if (v instanceof CharSequence) {
168+
output.append((CharSequence) v);
169+
return;
170+
}
171+
if (output instanceof OutputStream) {
172+
OutputStream os = (OutputStream) output;
173+
JSON.writeJSONString(os, StandardCharsets.UTF_8, v, SerializeConfig.globalInstance, filters, dateFormatPattern, DEFAULT_GENERATE_FEATURE, features);
174+
} else if (output instanceof Writer) {
175+
Writer w = (Writer) output;
176+
writeJson(w, v, SerializeConfig.globalInstance, filters, dateFormatPattern, DEFAULT_GENERATE_FEATURE, features);
177+
} else {
178+
writeJson(output.asWriter(), v, SerializeConfig.globalInstance, filters, dateFormatPattern, DEFAULT_GENERATE_FEATURE, features);
179+
}
180+
} catch (IOException e) {
181+
throw E.ioException(e);
182+
}
183+
}
184+
}
185+
186+
public static void configure(final App app) {
45187
SerializeConfig config = SerializeConfig.getGlobalInstance();
46188

47189
// patch https://github.com/alibaba/fastjson/issues/478
@@ -75,42 +217,41 @@ public static void configure(App app) {
75217
parserConfig.putDeserializer(ISObject.class, sObjectCodec);
76218
parserConfig.putDeserializer(SObject.class, sObjectCodec);
77219

78-
MvcConfig.jsonSerializer(new $.F1<Object, String>() {
220+
MvcConfig.jsonSerializer(new $.Func2<org.osgl.util.Output, Object, Void>() {
79221
@Override
80-
public String apply(Object o) throws NotAppliedException, Osgl.Break {
81-
if (null == o) {
82-
return "{}";
83-
}
84-
ActContext<?> ctx = ActContext.Base.currentContext();
85-
SerializeFilter[] filters = null;
86-
SerializerFeature[] features = null;
87-
String dateFormatPattern = null;
88-
if (null != ctx) {
89-
filters = ctx.fastjsonFilters();
90-
features = ctx.fastjsonFeatures();
91-
dateFormatPattern = ctx.dateFormatPattern();
92-
}
93-
Boolean b = DisableFastJsonCircularReferenceDetect.option.get();
94-
C.Set<SerializerFeature> featureSet = C.newSet();
95-
if (null != features) {
96-
featureSet.addAll(C.listOf(features));
97-
}
98-
featureSet.add(SerializerFeature.WriteDateUseDateFormat);
99-
if (null != b && b) {
100-
featureSet.add(SerializerFeature.DisableCircularReferenceDetect);
101-
}
102-
return null == dateFormatPattern ?
103-
JSON.toJSONString(
104-
o, filters,
105-
featureSet.toArray(new SerializerFeature[featureSet.size()])) :
106-
JSON.toJSONString(
107-
o,
108-
SerializeConfig.globalInstance,
109-
filters,
110-
dateFormatPattern,
111-
DEFAULT_GENERATE_FEATURE,
112-
featureSet.toArray(new SerializerFeature[featureSet.size()]));
222+
public Void apply(org.osgl.util.Output appendable, Object v) throws NotAppliedException, Osgl.Break {
223+
new JsonWriter(v, null, false, ActContext.Base.currentContext()).apply(appendable);
224+
return null;
113225
}
114226
});
115227
}
228+
229+
// FastJSON does not provide the API so we have to create our own
230+
private static final void writeJson(Writer os, //
231+
Object object, //
232+
SerializeConfig config, //
233+
SerializeFilter[] filters, //
234+
String dateFormat, //
235+
int defaultFeatures, //
236+
SerializerFeature... features) {
237+
SerializeWriter writer = new SerializeWriter(os, defaultFeatures, features);
238+
239+
try {
240+
JSONSerializer serializer = new JSONSerializer(writer, config);
241+
242+
if (dateFormat != null && dateFormat.length() != 0) {
243+
serializer.setDateFormat(dateFormat);
244+
serializer.config(SerializerFeature.WriteDateUseDateFormat, true);
245+
}
246+
247+
if (filters != null) {
248+
for (SerializeFilter filter : filters) {
249+
serializer.addFilter(filter);
250+
}
251+
}
252+
serializer.write(object);
253+
} finally {
254+
writer.close();
255+
}
256+
}
116257
}

0 commit comments

Comments
 (0)