|
15 | 15 | */
|
16 | 16 | package org.springframework.messaging.rsocket;
|
17 | 17 |
|
| 18 | +import java.util.concurrent.atomic.AtomicBoolean; |
18 | 19 | import java.util.function.Function;
|
19 | 20 |
|
20 | 21 | import io.rsocket.AbstractRSocket;
|
|
29 | 30 | import org.springframework.core.io.buffer.DataBuffer;
|
30 | 31 | import org.springframework.core.io.buffer.DataBufferFactory;
|
31 | 32 | import org.springframework.core.io.buffer.DataBufferUtils;
|
32 |
| -import org.springframework.core.io.buffer.PooledDataBuffer; |
| 33 | +import org.springframework.core.io.buffer.NettyDataBuffer; |
33 | 34 | import org.springframework.lang.Nullable;
|
34 | 35 | import org.springframework.messaging.Message;
|
35 | 36 | import org.springframework.messaging.MessageHeaders;
|
@@ -84,6 +85,9 @@ public Mono<Void> handleConnectionSetupPayload(ConnectionSetupPayload payload) {
|
84 | 85 | if (StringUtils.hasText(payload.dataMimeType())) {
|
85 | 86 | this.dataMimeType = MimeTypeUtils.parseMimeType(payload.dataMimeType());
|
86 | 87 | }
|
| 88 | + // frameDecoder does not apply to connectionSetupPayload |
| 89 | + // so retain here since handle expects it.. |
| 90 | + payload.retain(); |
87 | 91 | return handle(payload);
|
88 | 92 | }
|
89 | 93 |
|
@@ -120,54 +124,72 @@ public Mono<Void> metadataPush(Payload payload) {
|
120 | 124 |
|
121 | 125 |
|
122 | 126 | private Mono<Void> handle(Payload payload) {
|
123 |
| - Message<?> message = MessageBuilder.createMessage( |
124 |
| - Mono.fromCallable(() -> wrapPayloadData(payload)), createHeaders(payload, null)); |
| 127 | + String destination = getDestination(payload); |
| 128 | + MessageHeaders headers = createHeaders(destination, null); |
| 129 | + DataBuffer dataBuffer = retainDataAndReleasePayload(payload); |
| 130 | + int refCount = refCount(dataBuffer); |
| 131 | + Message<?> message = MessageBuilder.createMessage(dataBuffer, headers); |
| 132 | + return Mono.defer(() -> this.handler.apply(message)) |
| 133 | + .doFinally(s -> { |
| 134 | + if (refCount(dataBuffer) == refCount) { |
| 135 | + DataBufferUtils.release(dataBuffer); |
| 136 | + } |
| 137 | + }); |
| 138 | + } |
125 | 139 |
|
126 |
| - return this.handler.apply(message); |
| 140 | + private int refCount(DataBuffer dataBuffer) { |
| 141 | + return dataBuffer instanceof NettyDataBuffer ? |
| 142 | + ((NettyDataBuffer) dataBuffer).getNativeBuffer().refCnt() : 1; |
127 | 143 | }
|
128 | 144 |
|
129 | 145 | private Flux<Payload> handleAndReply(Payload firstPayload, Flux<Payload> payloads) {
|
130 | 146 | MonoProcessor<Flux<Payload>> replyMono = MonoProcessor.create();
|
131 |
| - Message<?> message = MessageBuilder.createMessage( |
132 |
| - payloads.map(this::wrapPayloadData).doOnDiscard(PooledDataBuffer.class, DataBufferUtils::release), |
133 |
| - createHeaders(firstPayload, replyMono)); |
134 |
| - |
135 |
| - return this.handler.apply(message) |
| 147 | + String destination = getDestination(firstPayload); |
| 148 | + MessageHeaders headers = createHeaders(destination, replyMono); |
| 149 | + |
| 150 | + AtomicBoolean read = new AtomicBoolean(); |
| 151 | + Flux<DataBuffer> buffers = payloads.map(this::retainDataAndReleasePayload).doOnSubscribe(s -> read.set(true)); |
| 152 | + Message<Flux<DataBuffer>> message = MessageBuilder.createMessage(buffers, headers); |
| 153 | + |
| 154 | + return Mono.defer(() -> this.handler.apply(message)) |
| 155 | + .doFinally(s -> { |
| 156 | + // Subscription should have happened by now due to ChannelSendOperator |
| 157 | + if (!read.get()) { |
| 158 | + buffers.subscribe(DataBufferUtils::release); |
| 159 | + } |
| 160 | + }) |
136 | 161 | .thenMany(Flux.defer(() -> replyMono.isTerminated() ?
|
137 | 162 | replyMono.flatMapMany(Function.identity()) :
|
138 | 163 | Mono.error(new IllegalStateException("Something went wrong: reply Mono not set"))));
|
139 | 164 | }
|
140 | 165 |
|
141 |
| - private MessageHeaders createHeaders(Payload payload, @Nullable MonoProcessor<?> replyMono) { |
| 166 | + private String getDestination(Payload payload) { |
142 | 167 |
|
143 | 168 | // TODO:
|
144 | 169 | // For now treat the metadata as a simple string with routing information.
|
145 | 170 | // We'll have to get more sophisticated once the routing extension is completed.
|
146 | 171 | // https://github.com/rsocket/rsocket-java/issues/568
|
147 | 172 |
|
148 |
| - MessageHeaderAccessor headers = new MessageHeaderAccessor(); |
| 173 | + return payload.getMetadataUtf8(); |
| 174 | + } |
149 | 175 |
|
150 |
| - String destination = payload.getMetadataUtf8(); |
151 |
| - headers.setHeader(DestinationPatternsMessageCondition.LOOKUP_DESTINATION_HEADER, destination); |
| 176 | + private DataBuffer retainDataAndReleasePayload(Payload payload) { |
| 177 | + return PayloadUtils.retainDataAndReleasePayload(payload, this.strategies.dataBufferFactory()); |
| 178 | + } |
152 | 179 |
|
| 180 | + private MessageHeaders createHeaders(String destination, @Nullable MonoProcessor<?> replyMono) { |
| 181 | + MessageHeaderAccessor headers = new MessageHeaderAccessor(); |
| 182 | + headers.setHeader(DestinationPatternsMessageCondition.LOOKUP_DESTINATION_HEADER, destination); |
153 | 183 | if (this.dataMimeType != null) {
|
154 | 184 | headers.setContentType(this.dataMimeType);
|
155 | 185 | }
|
156 |
| - |
157 | 186 | headers.setHeader(RSocketRequesterMethodArgumentResolver.RSOCKET_REQUESTER_HEADER, this.requester);
|
158 |
| - |
159 | 187 | if (replyMono != null) {
|
160 | 188 | headers.setHeader(RSocketPayloadReturnValueHandler.RESPONSE_HEADER, replyMono);
|
161 | 189 | }
|
162 |
| - |
163 | 190 | DataBufferFactory bufferFactory = this.strategies.dataBufferFactory();
|
164 | 191 | headers.setHeader(HandlerMethodReturnValueHandler.DATA_BUFFER_FACTORY_HEADER, bufferFactory);
|
165 |
| - |
166 | 192 | return headers.getMessageHeaders();
|
167 | 193 | }
|
168 | 194 |
|
169 |
| - private DataBuffer wrapPayloadData(Payload payload) { |
170 |
| - return PayloadUtils.wrapPayloadData(payload, this.strategies.dataBufferFactory()); |
171 |
| - } |
172 |
| - |
173 | 195 | }
|
0 commit comments