19
19
import java .lang .reflect .Method ;
20
20
import java .util .ArrayList ;
21
21
import java .util .Arrays ;
22
+ import java .util .Collections ;
22
23
import java .util .Comparator ;
23
24
import java .util .LinkedHashSet ;
24
25
import java .util .List ;
25
26
import java .util .Set ;
27
+ import java .util .function .Predicate ;
26
28
27
29
import org .springframework .beans .factory .config .ConfigurableBeanFactory ;
28
30
import org .springframework .context .ApplicationContext ;
29
31
import org .springframework .context .ConfigurableApplicationContext ;
30
32
import org .springframework .context .EmbeddedValueResolverAware ;
33
+ import org .springframework .context .SmartLifecycle ;
31
34
import org .springframework .core .annotation .AnnotatedElementUtils ;
32
35
import org .springframework .core .codec .Decoder ;
33
36
import org .springframework .core .convert .ConversionService ;
34
37
import org .springframework .format .support .DefaultFormattingConversionService ;
35
38
import org .springframework .lang .Nullable ;
36
39
import org .springframework .messaging .Message ;
40
+ import org .springframework .messaging .ReactiveSubscribableChannel ;
37
41
import org .springframework .messaging .handler .CompositeMessageCondition ;
38
42
import org .springframework .messaging .handler .DestinationPatternsMessageCondition ;
39
43
import org .springframework .messaging .handler .annotation .MessageMapping ;
51
55
import org .springframework .validation .Validator ;
52
56
53
57
/**
54
- * Extension of {@link AbstractMethodMessageHandler} for
55
- * {@link MessageMapping @MessageMapping} methods.
58
+ * Extension of {@link AbstractMethodMessageHandler} for reactive, non-blocking
59
+ * handling of messages via {@link MessageMapping @MessageMapping} methods.
60
+ * By default such methods are detected in {@code @Controller} Spring beans but
61
+ * that can be changed via {@link #setHandlerPredicate(Predicate)}.
56
62
*
57
- * <p>The payload of incoming messages is decoded through
58
- * {@link PayloadMethodArgumentResolver} using one of the configured
59
- * {@link #setDecoders(List)} decoders .
63
+ * <p>Payloads for incoming messages are decoded through the configured
64
+ * {@link #setDecoders(List)} decoders, with the help of
65
+ * {@link PayloadMethodArgumentResolver} .
60
66
*
61
- * <p>The {@link #setEncoderReturnValueHandler encoderReturnValueHandler}
62
- * property must be set to encode and handle return values from
63
- * {@code @MessageMapping} methods.
67
+ * <p>There is no default handling for return values but
68
+ * {@link #setReturnValueHandlerConfigurer} can be used to configure custom
69
+ * return value handlers. Sub-classes may also override
70
+ * {@link #initReturnValueHandlers()} to set up default return value handlers.
64
71
*
65
72
* @author Rossen Stoyanchev
66
73
* @since 5.2
74
+ * @see AbstractEncoderMethodReturnValueHandler
67
75
*/
68
76
public class MessageMappingMessageHandler extends AbstractMethodMessageHandler <CompositeMessageCondition >
69
- implements EmbeddedValueResolverAware {
77
+ implements SmartLifecycle , EmbeddedValueResolverAware {
70
78
71
- private PathMatcher pathMatcher = new AntPathMatcher () ;
79
+ private final ReactiveSubscribableChannel inboundChannel ;
72
80
73
81
private final List <Decoder <?>> decoders = new ArrayList <>();
74
82
75
83
@ Nullable
76
84
private Validator validator ;
77
85
78
- @ Nullable
79
- private HandlerMethodReturnValueHandler encoderReturnValueHandler ;
86
+ private PathMatcher pathMatcher ;
80
87
81
88
private ConversionService conversionService = new DefaultFormattingConversionService ();
82
89
83
90
@ Nullable
84
91
private StringValueResolver valueResolver ;
85
92
93
+ private volatile boolean running = false ;
86
94
87
- /**
88
- * Set the PathMatcher implementation to use for matching destinations
89
- * against configured destination patterns.
90
- * <p>By default, {@link AntPathMatcher} is used.
91
- */
92
- public void setPathMatcher (PathMatcher pathMatcher ) {
93
- Assert .notNull (pathMatcher , "PathMatcher must not be null" );
94
- this .pathMatcher = pathMatcher ;
95
- }
95
+ private final Object lifecycleMonitor = new Object ();
96
96
97
- /**
98
- * Return the PathMatcher implementation to use for matching destinations.
99
- */
100
- public PathMatcher getPathMatcher () {
101
- return this .pathMatcher ;
97
+
98
+ public MessageMappingMessageHandler (ReactiveSubscribableChannel inboundChannel ) {
99
+ Assert .notNull (inboundChannel , "`inboundChannel` is required" );
100
+ this .inboundChannel = inboundChannel ;
101
+ this .pathMatcher = new AntPathMatcher ();
102
+ ((AntPathMatcher ) this .pathMatcher ).setPathSeparator ("." );
103
+ setHandlerPredicate (beanType -> AnnotatedElementUtils .hasAnnotation (beanType , Controller .class ));
102
104
}
103
105
106
+
104
107
/**
105
- * Configure the decoders to user for incoming payloads.
108
+ * Configure the decoders to use for incoming payloads.
106
109
*/
107
110
public void setDecoders (List <? extends Decoder <?>> decoders ) {
108
111
this .decoders .addAll (decoders );
@@ -115,14 +118,6 @@ public List<? extends Decoder<?>> getDecoders() {
115
118
return this .decoders ;
116
119
}
117
120
118
- /**
119
- * Return the configured Validator instance.
120
- */
121
- @ Nullable
122
- public Validator getValidator () {
123
- return this .validator ;
124
- }
125
-
126
121
/**
127
122
* Set the Validator instance used for validating {@code @Payload} arguments.
128
123
* @see org.springframework.validation.annotation.Validated
@@ -133,27 +128,28 @@ public void setValidator(@Nullable Validator validator) {
133
128
}
134
129
135
130
/**
136
- * Configure the return value handler that will encode response content.
137
- * Consider extending {@link AbstractEncoderMethodReturnValueHandler} which
138
- * provides the infrastructure to encode and all that's left is to somehow
139
- * handle the encoded content, e.g. by wrapping as a message and passing it
140
- * to something or sending it somewhere.
141
- * <p>By default this is not configured in which case payload/content return
142
- * values from {@code @MessageMapping} methods will remain unhandled.
143
- * @param encoderReturnValueHandler the return value handler to use
144
- * @see AbstractEncoderMethodReturnValueHandler
131
+ * Return the configured Validator instance.
145
132
*/
146
- public void setEncoderReturnValueHandler (@ Nullable HandlerMethodReturnValueHandler encoderReturnValueHandler ) {
147
- this .encoderReturnValueHandler = encoderReturnValueHandler ;
133
+ @ Nullable
134
+ public Validator getValidator () {
135
+ return this .validator ;
148
136
}
149
137
150
138
/**
151
- * Return the configured
152
- * {@link #setEncoderReturnValueHandler encoderReturnValueHandler}.
139
+ * Set the PathMatcher implementation to use for matching destinations
140
+ * against configured destination patterns.
141
+ * <p>By default, {@link AntPathMatcher} is used with separator set to ".".
153
142
*/
154
- @ Nullable
155
- public HandlerMethodReturnValueHandler getEncoderReturnValueHandler () {
156
- return this .encoderReturnValueHandler ;
143
+ public void setPathMatcher (PathMatcher pathMatcher ) {
144
+ Assert .notNull (pathMatcher , "PathMatcher must not be null" );
145
+ this .pathMatcher = pathMatcher ;
146
+ }
147
+
148
+ /**
149
+ * Return the PathMatcher implementation to use for matching destinations.
150
+ */
151
+ public PathMatcher getPathMatcher () {
152
+ return this .pathMatcher ;
157
153
}
158
154
159
155
/**
@@ -204,20 +200,40 @@ protected List<? extends HandlerMethodArgumentResolver> initArgumentResolvers()
204
200
205
201
@ Override
206
202
protected List <? extends HandlerMethodReturnValueHandler > initReturnValueHandlers () {
207
- List <HandlerMethodReturnValueHandler > handlers = new ArrayList <>();
208
- handlers .addAll (getReturnValueHandlerConfigurer ().getCustomHandlers ());
209
- if (this .encoderReturnValueHandler != null ) {
210
- handlers .add (this .encoderReturnValueHandler );
203
+ return Collections .emptyList ();
204
+ }
205
+
206
+
207
+ @ Override
208
+ public final void start () {
209
+ synchronized (this .lifecycleMonitor ) {
210
+ this .inboundChannel .subscribe (this );
211
+ this .running = true ;
211
212
}
212
- return handlers ;
213
213
}
214
214
215
+ @ Override
216
+ public final void stop () {
217
+ synchronized (this .lifecycleMonitor ) {
218
+ this .running = false ;
219
+ this .inboundChannel .unsubscribe (this );
220
+ }
221
+ }
222
+
223
+ @ Override
224
+ public final void stop (Runnable callback ) {
225
+ synchronized (this .lifecycleMonitor ) {
226
+ stop ();
227
+ callback .run ();
228
+ }
229
+ }
215
230
216
231
@ Override
217
- protected boolean isHandler ( Class <?> beanType ) {
218
- return AnnotatedElementUtils . hasAnnotation ( beanType , Controller . class ) ;
232
+ public final boolean isRunning ( ) {
233
+ return this . running ;
219
234
}
220
235
236
+
221
237
@ Override
222
238
protected CompositeMessageCondition getMappingForMethod (Method method , Class <?> handlerType ) {
223
239
CompositeMessageCondition methodCondition = getCondition (method );
0 commit comments