All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.sonus21.rqueue.converter.GenericMessageConverter Maven / Gradle / Ivy

/*
 *  Copyright 2021 Sonu Kumar
 *
 *  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 com.github.sonus21.rqueue.converter;

import static org.springframework.util.Assert.notNull;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.sonus21.rqueue.utils.SerializationUtils;
import java.lang.reflect.TypeVariable;
import java.util.Collection;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.converter.SmartMessageConverter;
import org.springframework.messaging.support.GenericMessage;

/**
 * A converter to turn the payload of a {@link Message} from serialized form to a typed String and
 * vice versa. This class does not support generic class except {@link List},even for list the
 * entries should be non generic.
 */
@Slf4j
public class GenericMessageConverter implements SmartMessageConverter {
  private final SmartMessageSerDes smartMessageSerDes;

  public GenericMessageConverter() {
    ObjectMapper mapper = SerializationUtils.createObjectMapper();
    this.smartMessageSerDes = new SmartMessageSerDes(mapper);
  }

  public GenericMessageConverter(ObjectMapper objectMapper) {
    notNull(objectMapper, "objectMapper cannot be null");
    this.smartMessageSerDes = new SmartMessageSerDes(objectMapper);
  }

  /**
   * Create a {@link Message} whose payload is the result of converting the given payload Object to
   * serialized form. It ignores all headers components.
   *
   * 

If the converter cannot perform the conversion, it return {@code null}. * * @param payload the Object to convert * @param headers optional headers for the message (may be {@code null}) * @return the new message, or {@code null} */ @Override public Message toMessage(Object payload, MessageHeaders headers) { log.debug("Payload: {} headers: {}", payload, headers); String message = smartMessageSerDes.serialize(payload); if (message == null) { return null; } return new GenericMessage<>(message); } @Override public Object fromMessage(Message message, Class targetClass, Object conversionHint) { log.debug("Message: {} class: {} hint: {}", message, targetClass, conversionHint); return fromMessage(message, targetClass); } @Override public Message toMessage(Object payload, MessageHeaders headers, Object conversionHint) { log.debug("Payload: {} headers: {} hint: {}", payload, headers, conversionHint); return toMessage(payload, headers); } /** * Convert the payload of a {@link Message} from a serialized form to a typed Object of type * stored in message it self. * *

If the converter cannot perform the conversion it returns {@code null}. * * @param message the input message * @param targetClass the target class for the conversion * @return the result of the conversion, or {@code null} if the converter cannot perform the * conversion. */ @Override public Object fromMessage(Message message, Class targetClass) { log.debug("Message: {} class: {}", message, targetClass); String payload; try { payload = (String) message.getPayload(); } catch (ClassCastException e) { return null; } return smartMessageSerDes.deserialize(payload); } @Getter @Setter @NoArgsConstructor @AllArgsConstructor private static class Msg { private String msg; private String name; } public static class SmartMessageSerDes { private final ObjectMapper objectMapper; public SmartMessageSerDes(ObjectMapper objectMapper) { this.objectMapper = objectMapper; } private String[] splitClassNames(String name) { return name.split("#"); } private String getClassNameForCollection(String name, Collection payload) { if (payload instanceof List) { if (payload.isEmpty()) { return null; } String itemClassName = getClassName(((List) payload).get(0)); if (itemClassName == null) { return null; } return name + '#' + itemClassName; } return null; } private String getGenericFieldBasedClassName(Class clazz) { TypeVariable[] typeVariables = clazz.getTypeParameters(); if (typeVariables.length == 0) { return clazz.getName(); } return null; } private String getClassName(Object payload) { Class payloadClass = payload.getClass(); String name = payloadClass.getName(); if (payload instanceof Collection) { return getClassNameForCollection(name, (Collection) payload); } return getGenericFieldBasedClassName(payloadClass); } private JavaType getTargetType(Msg msg) throws ClassNotFoundException { String[] classNames = splitClassNames(msg.getName()); if (classNames.length == 1) { Class c = Thread.currentThread().getContextClassLoader().loadClass(msg.getName()); return objectMapper.getTypeFactory().constructType(c); } Class envelopeClass = Thread.currentThread().getContextClassLoader().loadClass(classNames[0]); Class[] classes = new Class[classNames.length - 1]; for (int i = 1; i < classNames.length; i++) { classes[i - 1] = Thread.currentThread().getContextClassLoader().loadClass(classNames[i]); } return objectMapper.getTypeFactory().constructParametricType(envelopeClass, classes); } public Object deserialize(String payload) { try { if (SerializationUtils.isJson(payload)) { Msg msg = objectMapper.readValue(payload, Msg.class); JavaType type = getTargetType(msg); return objectMapper.readValue(msg.msg, type); } } catch (Exception e) { log.warn("Deserialization of message {} failed", payload, e); } return null; } public T deserialize(byte[] payload, Class clazz) { if (SerializationUtils.isEmpty(payload)) { return null; } try { return objectMapper.readValue(payload, clazz); } catch (Exception e) { log.warn("Deserialization of message {} failed", new String(payload), e); } return null; } public String serialize(Object payload) { String name = getClassName(payload); if (name == null) { return null; } try { String msg = objectMapper.writeValueAsString(payload); Msg message = new Msg(msg, name); return objectMapper.writeValueAsString(message); } catch (JsonProcessingException e) { log.warn("Serialisation failed", e); return null; } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy