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

io.gravitee.exchange.api.websocket.command.DefaultExchangeSerDe Maven / Gradle / Ivy

/*
 * Copyright © 2015 The Gravitee team (http://gravitee.io)
 *
 * 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
 *
 *     http://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 io.gravitee.exchange.api.websocket.command;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.exc.InvalidFormatException;
import com.fasterxml.jackson.databind.exc.InvalidTypeIdException;
import com.fasterxml.jackson.databind.jsontype.NamedType;
import io.gravitee.exchange.api.command.Command;
import io.gravitee.exchange.api.command.Exchange;
import io.gravitee.exchange.api.command.Reply;
import io.gravitee.exchange.api.command.goodbye.GoodByeCommand;
import io.gravitee.exchange.api.command.goodbye.GoodByeReply;
import io.gravitee.exchange.api.command.healtcheck.HealthCheckCommand;
import io.gravitee.exchange.api.command.healtcheck.HealthCheckReply;
import io.gravitee.exchange.api.command.hello.HelloCommand;
import io.gravitee.exchange.api.command.hello.HelloReply;
import io.gravitee.exchange.api.command.noreply.NoReply;
import io.gravitee.exchange.api.command.primary.PrimaryCommand;
import io.gravitee.exchange.api.command.primary.PrimaryReply;
import io.gravitee.exchange.api.command.unknown.UnknownCommand;
import io.gravitee.exchange.api.command.unknown.UnknownReply;
import io.gravitee.exchange.api.websocket.command.exception.DeserializationException;
import io.gravitee.exchange.api.websocket.command.exception.SerializationException;
import io.gravitee.exchange.api.websocket.protocol.ProtocolVersion;
import io.gravitee.exchange.api.websocket.protocol.legacy.ignored.IgnoredReply;
import java.util.HashMap;
import java.util.Map;

/**
 * @author Guillaume LAMIRAND (guillaume.lamirand at graviteesource.com)
 * @author GraviteeSource Team
 */
public class DefaultExchangeSerDe implements ExchangeSerDe {

    private static final Map>> DEFAULT_COMMAND_TYPE = Map.of(
        // Command
        HelloCommand.COMMAND_TYPE,
        HelloCommand.class,
        GoodByeCommand.COMMAND_TYPE,
        GoodByeCommand.class,
        io.gravitee.exchange.api.websocket.protocol.legacy.hello.HelloCommand.COMMAND_TYPE,
        io.gravitee.exchange.api.websocket.protocol.legacy.hello.HelloCommand.class,
        io.gravitee.exchange.api.websocket.protocol.legacy.goodbye.GoodByeCommand.COMMAND_TYPE,
        io.gravitee.exchange.api.websocket.protocol.legacy.goodbye.GoodByeCommand.class,
        HealthCheckCommand.COMMAND_TYPE,
        HealthCheckCommand.class,
        PrimaryCommand.COMMAND_TYPE,
        PrimaryCommand.class,
        UnknownCommand.COMMAND_TYPE,
        UnknownCommand.class
    );

    private static final Map>> DEFAULT_REPLY_TYPE = Map.of(
        HelloCommand.COMMAND_TYPE,
        HelloReply.class,
        GoodByeCommand.COMMAND_TYPE,
        GoodByeReply.class,
        io.gravitee.exchange.api.websocket.protocol.legacy.hello.HelloReply.COMMAND_TYPE,
        io.gravitee.exchange.api.websocket.protocol.legacy.hello.HelloReply.class,
        io.gravitee.exchange.api.websocket.protocol.legacy.goodbye.GoodByeReply.COMMAND_TYPE,
        io.gravitee.exchange.api.websocket.protocol.legacy.goodbye.GoodByeReply.class,
        HealthCheckCommand.COMMAND_TYPE,
        HealthCheckReply.class,
        PrimaryCommand.COMMAND_TYPE,
        PrimaryReply.class,
        NoReply.COMMAND_TYPE,
        NoReply.class,
        IgnoredReply.COMMAND_TYPE,
        IgnoredReply.class,
        UnknownCommand.COMMAND_TYPE,
        UnknownReply.class
    );
    private final ObjectMapper objectMapper;

    public DefaultExchangeSerDe(final ObjectMapper objectMapper) {
        this(objectMapper, null, null);
    }

    public DefaultExchangeSerDe(
        final ObjectMapper objectMapper,
        final Map>> customCommandTypes,
        final Map>> customReplyTypes
    ) {
        this.objectMapper =
            objectMapper
                .enable(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE)
                .disable(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES)
                .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
                .setSerializationInclusion(JsonInclude.Include.NON_NULL);

        registerCommandTypes(objectMapper, customCommandTypes);
        registerReplyTypes(objectMapper, customReplyTypes);
    }

    private void registerCommandTypes(final ObjectMapper objectMapper, final Map>> customCommandTypes) {
        Map>> commandTypes = new HashMap<>(DEFAULT_COMMAND_TYPE);
        if (customCommandTypes != null) {
            commandTypes.putAll(customCommandTypes);
        }
        commandTypes.forEach((type, aClass) -> objectMapper.registerSubtypes(new NamedType(aClass, type)));
    }

    private void registerReplyTypes(final ObjectMapper objectMapper, final Map>> customReplyTypes) {
        Map>> replyTypes = new HashMap<>(DEFAULT_REPLY_TYPE);
        if (customReplyTypes != null) {
            replyTypes.putAll(customReplyTypes);
        }
        replyTypes.forEach((type, aClass) -> objectMapper.registerSubtypes(new NamedType(aClass, type)));
    }

    @Override
    public > C deserializeAsCommand(final ProtocolVersion protocolVersion, final String dataType, final String data)
        throws DeserializationException {
        return (C) readCommand(data, Command.class);
    }

    protected > C readCommand(final String data, Class clazz) {
        try {
            return readJson(data, clazz);
        } catch (InvalidTypeIdException e) {
            return (C) unknownCommand();
        } catch (JsonProcessingException e) {
            throw new DeserializationException(e);
        }
    }

    protected Command unknownCommand() {
        return new UnknownCommand();
    }

    @Override
    public > R deserializeAsReply(final ProtocolVersion protocolVersion, final String dataType, final String data)
        throws DeserializationException {
        return (R) readReply(data, Reply.class);
    }

    protected > R readReply(final String data, Class clazz) {
        try {
            return readJson(data, clazz);
        } catch (InvalidTypeIdException e) {
            return (R) unknownReply();
        } catch (JsonProcessingException e) {
            throw new DeserializationException(e);
        }
    }

    private static UnknownReply unknownReply() {
        return new UnknownReply(null, "Unknown reply type");
    }

    protected  T readJson(final String jsonAsString, Class clazz) throws JsonProcessingException {
        if (jsonAsString != null) {
            return objectMapper.readValue(jsonAsString, clazz);
        }
        return null;
    }

    @Override
    public String serialize(final ProtocolVersion protocolVersion, final Exchange exchange) throws SerializationException {
        return writeJson(exchange);
    }

    protected String writeJson(final Object object) {
        try {
            if (object != null) {
                return objectMapper.writeValueAsString(object);
            }
            return null;
        } catch (JsonProcessingException e) {
            throw new SerializationException(e);
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy