11package org .hswebframework .web .starter .jackson ;
22
3+ import lombok .AllArgsConstructor ;
4+ import org .hswebframework .web .authorization .Authentication ;
5+ import org .hswebframework .web .authorization .AuthenticationHolder ;
6+ import org .hswebframework .web .authorization .simple .SimpleAuthentication ;
37import org .hswebframework .web .i18n .LocaleUtils ;
48import org .springframework .http .codec .json .Jackson2CodecSupport ;
59
610import java .io .IOException ;
711import java .lang .annotation .Annotation ;
812import java .nio .charset .Charset ;
913import java .util .*;
14+ import java .util .concurrent .Callable ;
15+ import java .util .function .Function ;
1016
1117import com .fasterxml .jackson .core .JsonEncoding ;
1218import com .fasterxml .jackson .core .JsonGenerator ;
3743import org .springframework .util .Assert ;
3844import org .springframework .util .MimeType ;
3945
46+ import javax .annotation .Nonnull ;
47+
4048/**
4149 * Base class providing support methods for Jackson 2.9 encoding. For non-streaming use
4250 * cases, {@link Flux} elements are collected into a {@link List} before serialization for
@@ -106,71 +114,80 @@ public boolean canEncode(ResolvableType elementType, @Nullable MimeType mimeType
106114 }
107115 }
108116 return (Object .class == clazz ||
109- (!String .class .isAssignableFrom (elementType .resolve (clazz )) && getObjectMapper ().canSerialize (clazz )));
117+ (!String .class .isAssignableFrom (elementType .resolve (clazz )) && getObjectMapper ().canSerialize (clazz )));
110118 }
111119
120+
112121 @ Override
113- public Flux <DataBuffer > encode (Publisher <?> inputStream , DataBufferFactory bufferFactory ,
114- ResolvableType elementType , @ Nullable MimeType mimeType , @ Nullable Map <String , Object > hints ) {
122+ @ Nonnull
123+ public Flux <DataBuffer > encode (@ Nonnull Publisher <?> inputStream , @ Nonnull DataBufferFactory bufferFactory ,
124+ @ Nonnull ResolvableType elementType , @ Nullable MimeType mimeType , @ Nullable Map <String , Object > hints ) {
115125
116126 Assert .notNull (inputStream , "'inputStream' must not be null" );
117127 Assert .notNull (bufferFactory , "'bufferFactory' must not be null" );
118128 Assert .notNull (elementType , "'elementType' must not be null" );
119129
130+
120131 if (inputStream instanceof Mono ) {
121- return Mono .from (inputStream )
122- .as (LocaleUtils ::transform )
123- .map (value -> encodeValue (value , bufferFactory , elementType , mimeType , hints ))
124- .flux ();
132+ return Mono
133+ .zip (
134+ currentContext (hints ),
135+ Mono .from (inputStream ),
136+ (ctx , value ) -> ctx
137+ .execute (() -> encodeValue (value , bufferFactory , elementType , mimeType , hints ))
138+ )
139+ .flux ();
125140 } else {
126141 byte [] separator = streamSeparator (mimeType );
127142 if (separator != null ) { // streaming
128143 try {
129144 ObjectWriter writer = createObjectWriter (elementType , mimeType , hints );
130145 ByteArrayBuilder byteBuilder = new ByteArrayBuilder (writer
131- .getFactory ()
132- ._getBufferRecycler ());
146+ .getFactory ()
147+ ._getBufferRecycler ());
133148 JsonEncoding encoding = getJsonEncoding (mimeType );
134149 JsonGenerator generator = getObjectMapper ()
135- .getFactory ()
136- .createGenerator (byteBuilder , encoding );
150+ .getFactory ()
151+ .createGenerator (byteBuilder , encoding );
137152 SequenceWriter sequenceWriter = writer .writeValues (generator );
138153
139- return Flux
140- .from (inputStream )
141- .as (LocaleUtils ::transform )
142- .map (value -> this .encodeStreamingValue (value ,
143- bufferFactory ,
144- hints ,
145- sequenceWriter ,
146- byteBuilder ,
147- separator ))
148- .doAfterTerminate (() -> {
149- try {
150- byteBuilder .release ();
151- generator .close ();
152- } catch (IOException ex ) {
153- logger .error ("Could not close Encoder resources" , ex );
154- }
155- });
154+ return currentContext (hints )
155+ .flatMapMany (ctx -> ctx
156+ .transform (inputStream ,
157+ value -> this
158+ .encodeStreamingValue (value ,
159+ bufferFactory ,
160+ hints ,
161+ sequenceWriter ,
162+ byteBuilder ,
163+ separator )))
164+
165+ .doAfterTerminate (() -> {
166+ try {
167+ byteBuilder .release ();
168+ generator .close ();
169+ } catch (IOException ex ) {
170+ logger .error ("Could not close Encoder resources" , ex );
171+ }
172+ });
156173 } catch (IOException ex ) {
157174 return Flux .error (ex );
158175 }
159176 } else { // non-streaming
160177 ResolvableType listType = ResolvableType .forClassWithGenerics (List .class , elementType );
161- return Flux .from (inputStream )
162- .collectList ()
163- .as (LocaleUtils ::transform )
164- .map (value -> encodeValue (value , bufferFactory , listType , mimeType , hints ))
165- .flux ();
178+ return currentContext (hints )
179+ .flatMapMany (ctx -> ctx
180+ .transform (Flux .from (inputStream ).collectList (),
181+ value -> encodeValue (value , bufferFactory , listType , mimeType , hints )));
166182 }
167183
168184 }
169185 }
170186
171187 @ Override
172- public DataBuffer encodeValue (Object value , DataBufferFactory bufferFactory ,
173- ResolvableType valueType , @ Nullable MimeType mimeType , @ Nullable Map <String , Object > hints ) {
188+ @ Nonnull
189+ public DataBuffer encodeValue (@ Nonnull Object value ,@ Nonnull DataBufferFactory bufferFactory ,
190+ @ Nonnull ResolvableType valueType , @ Nullable MimeType mimeType , @ Nullable Map <String , Object > hints ) {
174191
175192 ObjectWriter writer = createObjectWriter (valueType , mimeType , hints );
176193 ByteArrayBuilder byteBuilder = new ByteArrayBuilder (writer .getFactory ()._getBufferRecycler ());
@@ -251,7 +268,7 @@ private ObjectWriter createObjectWriter(ResolvableType valueType, @Nullable Mime
251268 JavaType javaType = getJavaType (valueType .getType (), null );
252269 Class <?> jsonView = (hints != null ? (Class <?>) hints .get (Jackson2CodecSupport .JSON_VIEW_HINT ) : null );
253270 ObjectWriter writer = (jsonView != null ?
254- getObjectMapper ().writerWithView (jsonView ) : getObjectMapper ().writer ());
271+ getObjectMapper ().writerWithView (jsonView ) : getObjectMapper ().writer ());
255272
256273 if (javaType .isContainerType ()) {
257274 writer = writer .forType (javaType );
@@ -321,4 +338,32 @@ public Map<String, Object> getEncodeHints(@Nullable ResolvableType actualType, R
321338 protected <A extends Annotation > A getAnnotation (MethodParameter parameter , Class <A > annotType ) {
322339 return parameter .getMethodAnnotation (annotType );
323340 }
341+
342+ static final SimpleAuthentication ANONYMOUS = new SimpleAuthentication ();
343+
344+ static Mono <EncodingContext > currentContext (Map <String , Object > hints ) {
345+ return Mono
346+ .zip (Authentication .currentReactive ().defaultIfEmpty (ANONYMOUS ),
347+ LocaleUtils .currentReactive (), EncodingContext ::new );
348+ }
349+
350+ @ AllArgsConstructor
351+ static class EncodingContext {
352+ private final Authentication authentication ;
353+ private final Locale locale ;
354+
355+ private <T , R > Flux <T > transform (Publisher <R > source , Function <R , T > transformer ) {
356+ return Flux
357+ .from (source )
358+ .map ((val ) -> execute (() -> transformer .apply (val )));
359+ }
360+
361+ private <T > T execute (Callable <T > callable ) {
362+ if (authentication == null || authentication == ANONYMOUS ) {
363+ return LocaleUtils .doWith (locale , callable );
364+ }
365+ return AuthenticationHolder
366+ .executeWith (authentication , () -> LocaleUtils .doWith (locale , callable ));
367+ }
368+ }
324369}
0 commit comments