INT-3045: Add in & out ZeroMq channel adapters (#3388)
* INT-3045: Add in & out ZeroMq channel adapters JIRA: https://jira.spring.io/browse/INT-3045 * Add `ZeroMqMessageHandler` to produce messages into one-way ZeroMq sockets * Add `ZeroMqMessageProducer` to consumer messages from one-way ZeroMq sockets * Add `ConvertingBytesMessageMapper` impl for the `BytesMessageMapper` to delegate an actual conversion into the provided `MessageConverter` * Add `ZeroMqHeaders` for message headers constants representing ZeroMq message attributes * Fix `ZeroMqChannel` for the proper deferred `zeroMqProxy` evaluation * Add more JavaDocs * Fix `ZeroMqChannelTests.testPubSubBind()` to be sure that really all the subscribed channels get the same message from the `PUB` socket * * Fix typo in the `ConvertingBytesMessageMapper` * Add `this` for `doOnError()` in the `ZeroMqChannel` & `ZeroMqMessageProducer` * Change the bind logic in the `ZeroMqMessageProducer` to `port` and let it to bind to random port. The actual port is available later via `getBoundPort()` * Introduce a `ZeroMqMessageProducer.receiveRaw()` to let received `ZMsg` to be produce as a `payload` * Add a logic into `ZeroMqMessageHandler` to treat `ZMsg` in the payload of request message as is without any conversion * Fix race condition in the `ZeroMqMessageProducer` to destroy `consumerScheduler` when the main `Flux` is complete * * Add Java DSL for ZeroMq components * Extract `ReactiveMessageHandlerSpec` for `ReactiveMessageHandler` impls * Add debug message into `EmbeddedJsonHeadersMessageMapper` when cannot `decodeNativeFormat()` * Make `ReactiveMongoDbMessageHandlerSpec` extending `ReactiveMessageHandlerSpec` * Make `ZeroMqProxy` `autoStartup` by default * Add `ZeroMqDslTests` to cover all the Java DSL for ZeroMq * Introduce a `MimeTypeSerializer` to serialize a `MimeType` into JSON as a plain string; use it as extra serializer in the `JacksonJsonUtils.messagingAwareMapper()` * Fix typo for the `AllowListTypeResolverBuilder` inner class * * Add some docs * Fix Checkstyle violations * * More docs * Fix language in Docs Co-authored-by: Gary Russell <grussell@vmware.com> Co-authored-by: Gary Russell <grussell@vmware.com>
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.dsl;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.integration.handler.ReactiveMessageHandlerAdapter;
|
||||
import org.springframework.messaging.ReactiveMessageHandler;
|
||||
|
||||
/**
|
||||
* The {@link MessageHandlerSpec} extension for {@link ReactiveMessageHandler}.
|
||||
*
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 5.4
|
||||
*/
|
||||
public abstract class ReactiveMessageHandlerSpec<S extends ReactiveMessageHandlerSpec<S, H>, H extends ReactiveMessageHandler>
|
||||
extends MessageHandlerSpec<S, ReactiveMessageHandlerAdapter>
|
||||
implements ComponentsRegistration {
|
||||
|
||||
protected final H reactiveMessageHandler; // NOSONAR - final
|
||||
|
||||
protected ReactiveMessageHandlerSpec(H reactiveMessageHandler) {
|
||||
this.reactiveMessageHandler = reactiveMessageHandler;
|
||||
this.target = new ReactiveMessageHandlerAdapter(this.reactiveMessageHandler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Object, String> getComponentsToRegister() {
|
||||
return Collections.singletonMap(this.reactiveMessageHandler, null);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright 2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.mapping;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageHeaders;
|
||||
import org.springframework.messaging.converter.MessageConverter;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* The {@link BytesMessageMapper} implementation to delegate to/from {@link Message}
|
||||
* conversion into the provided {@link MessageConverter}.
|
||||
* <p>
|
||||
* The {@link MessageConverter} must not return {@code null} from its
|
||||
* {@link MessageConverter#fromMessage(Message, Class)} and {@link MessageConverter#toMessage(Object, MessageHeaders)}
|
||||
* methods.
|
||||
* <p>
|
||||
* If {@link MessageConverter#fromMessage(Message, Class)} returns {@link String}, it is converted to {@link byte[]}
|
||||
* using a {@link StandardCharsets#UTF_8} encoding.
|
||||
*
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 5.4
|
||||
*/
|
||||
public class ConvertingBytesMessageMapper implements BytesMessageMapper {
|
||||
|
||||
private final MessageConverter messageConverter;
|
||||
|
||||
public ConvertingBytesMessageMapper(MessageConverter messageConverter) {
|
||||
Assert.notNull(messageConverter, "'messageConverter' must not be null");
|
||||
this.messageConverter = messageConverter;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public Message<?> toMessage(byte[] bytes, @Nullable Map<String, Object> headers) {
|
||||
MessageHeaders messageHeaders = null;
|
||||
if (headers != null) {
|
||||
messageHeaders = new MessageHeaders(headers);
|
||||
}
|
||||
Message<?> message = this.messageConverter.toMessage(bytes, messageHeaders);
|
||||
Assert.state(message != null, () ->
|
||||
"the '" + this.messageConverter + "' produced null for bytes:" + Arrays.toString(bytes));
|
||||
return message;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public byte[] fromMessage(Message<?> message) {
|
||||
Object result = this.messageConverter.fromMessage(message, byte[].class);
|
||||
Assert.state(result != null, () -> "the '" + this.messageConverter + "' produced null for message: " + message);
|
||||
return result instanceof String
|
||||
? ((String) result).getBytes(StandardCharsets.UTF_8)
|
||||
: (byte[]) result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -224,16 +224,14 @@ public class EmbeddedJsonHeadersMessageMapper implements BytesMessageMapper {
|
||||
message = decodeNativeFormat(bytes, headers);
|
||||
}
|
||||
catch (@SuppressWarnings("unused") Exception e) {
|
||||
// empty
|
||||
this.logger.debug("Failed to decode native format", e);
|
||||
}
|
||||
if (message == null) {
|
||||
try {
|
||||
message = (Message<?>) this.objectMapper.readValue(bytes, Object.class);
|
||||
}
|
||||
catch (Exception e) {
|
||||
if (this.logger.isDebugEnabled()) {
|
||||
this.logger.debug("Failed to decode JSON", e);
|
||||
}
|
||||
this.logger.debug("Failed to decode JSON", e);
|
||||
}
|
||||
}
|
||||
if (message != null) {
|
||||
|
||||
@@ -67,7 +67,7 @@ public final class JacksonJsonUtils {
|
||||
if (JacksonPresent.isJackson2Present()) {
|
||||
ObjectMapper mapper = new Jackson2JsonObjectMapper().getObjectMapper();
|
||||
|
||||
mapper.setDefaultTyping(new AllowlistTypeResolverBuilder(trustedPackages));
|
||||
mapper.setDefaultTyping(new AllowListTypeResolverBuilder(trustedPackages));
|
||||
|
||||
GenericMessageJacksonDeserializer genericMessageDeserializer = new GenericMessageJacksonDeserializer();
|
||||
genericMessageDeserializer.setMapper(mapper);
|
||||
@@ -83,6 +83,7 @@ public final class JacksonJsonUtils {
|
||||
|
||||
SimpleModule simpleModule = new SimpleModule()
|
||||
.addSerializer(new MessageHeadersJacksonSerializer())
|
||||
.addSerializer(new MimeTypeSerializer())
|
||||
.addDeserializer(GenericMessage.class, genericMessageDeserializer)
|
||||
.addDeserializer(ErrorMessage.class, errorMessageDeserializer)
|
||||
.addDeserializer(AdviceMessage.class, adviceMessageDeserializer)
|
||||
@@ -107,13 +108,13 @@ public final class JacksonJsonUtils {
|
||||
*
|
||||
* @since 4.3.11
|
||||
*/
|
||||
private static final class AllowlistTypeResolverBuilder extends ObjectMapper.DefaultTypeResolverBuilder {
|
||||
private static final class AllowListTypeResolverBuilder extends ObjectMapper.DefaultTypeResolverBuilder {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final String[] trustedPackages;
|
||||
|
||||
AllowlistTypeResolverBuilder(String... trustedPackages) {
|
||||
AllowListTypeResolverBuilder(String... trustedPackages) {
|
||||
super(ObjectMapper.DefaultTyping.NON_FINAL,
|
||||
//we do explicit validation in the TypeIdResolver
|
||||
BasicPolymorphicTypeValidator.builder()
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.support.json;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.springframework.util.MimeType;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.databind.JsonSerializer;
|
||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
|
||||
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
|
||||
|
||||
/**
|
||||
* Simple {@link JsonSerializer} extension to represent a {@link MimeType} object in the
|
||||
* target JSON as a plain string.
|
||||
*
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 5.4
|
||||
*/
|
||||
public class MimeTypeSerializer extends StdSerializer<MimeType> {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public MimeTypeSerializer() {
|
||||
super(MimeType.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serializeWithType(MimeType value, JsonGenerator generator, SerializerProvider serializers,
|
||||
TypeSerializer typeSer) throws IOException {
|
||||
|
||||
serialize(value, generator, serializers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(MimeType value, JsonGenerator generator, SerializerProvider provider) throws IOException {
|
||||
generator.writeString(value.toString());
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user