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

com.exactpro.sf.services.tcpip.InternalJsonCodec Maven / Gradle / Ivy

There is a newer version: 3.4.260
Show newest version
/******************************************************************************
 * Copyright 2009-2021 Exactpro (Exactpro Systems Limited)
 *
 * 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 com.exactpro.sf.services.tcpip;

import java.time.temporal.Temporal;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;

import org.apache.commons.codec.binary.Hex;
import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolDecoderOutput;
import org.apache.mina.filter.codec.ProtocolEncoderOutput;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.exactpro.sf.common.codecs.AbstractCodec;
import com.exactpro.sf.common.messages.IMessage;
import com.exactpro.sf.common.messages.IMessageFactory;
import com.exactpro.sf.common.messages.MessageUtil;
import com.exactpro.sf.common.messages.structures.IDictionaryStructure;
import com.exactpro.sf.common.util.ICommonSettings;
import com.exactpro.sf.services.IServiceContext;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.BasicPolymorphicTypeValidator;
import com.fasterxml.jackson.databind.jsontype.PolymorphicTypeValidator;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;

/**
 * Codec implements encode / decode message between internal tools by JSON protocol
 * @author nikita.smirnov
 */
public class InternalJsonCodec extends AbstractCodec {

    private static final Logger logger = LoggerFactory.getLogger(InternalJsonCodec.class);

    private static final int HEADER_SIZE = 8 * 2;

    private final ObjectMapper objectMapper;
    private final AtomicLong sequence = new AtomicLong(1);

    private IMessageFactory msgFactory;

    public InternalJsonCodec() {
        PolymorphicTypeValidator ptv = BasicPolymorphicTypeValidator
                .builder()
                .allowIfSubType(Map.class)
                .allowIfSubType(IMessage.class)
                .allowIfSubType(Temporal.class)
                .build();
        objectMapper = new ObjectMapper()
                .activateDefaultTyping(ptv)
                .registerModule(new JavaTimeModule());
    }

    @Override
    public void encode(IoSession session, Object message, ProtocolEncoderOutput out) throws Exception {
        if (message instanceof IMessage) {
            Map map = MessageUtil.convertToHashMap((IMessage)message);
            String json = objectMapper.writeValueAsString(map);

            long seq = sequence.getAndIncrement();

            IoBuffer buffer = IoBuffer.allocate(json.length() + HEADER_SIZE);
            buffer.putLong(json.length());
            buffer.putLong(seq);
            buffer.put(json.getBytes());

            out.write(buffer.flip());

            ((IMessage) message).getMetaData().setRawMessage(buffer.array());

            if (logger.isDebugEnabled()) {
                logger.debug("encode() sequnce {} as hex [{}]", seq, buffer.getHexDump());
            }
        } else {
            out.write(message);
        }
    }

    @Override
    public void init(IServiceContext serviceContext, ICommonSettings settings, IMessageFactory msgFactory, IDictionaryStructure dictionary) {
        super.init(serviceContext, settings, msgFactory, dictionary);
        if (msgFactory == null) {
            throw new IllegalArgumentException("Message factory can't be null");
        }

        this.msgFactory = msgFactory;
    }

    @Override
    protected boolean doDecodeInternal(IoSession session, IoBuffer in, ProtocolDecoderOutput out) throws Exception {
        boolean decoded = false;

        while (in.remaining() > HEADER_SIZE) {
            in.mark();
            long length = in.getLong();
            long seq = in.getLong();

            if (in.remaining() >= length) {
                byte[] buff = new byte[(int) length];
                in.get(buff);

                if (logger.isDebugEnabled()) {
                    logger.debug("decode() sequnce {} as hex [{}]", seq, Hex.encodeHex(buff));
                }
                decoded = true;

                in.reset();
                byte[] rawData = new byte[(int) length + HEADER_SIZE];
                in.get(rawData);

                IMessage message = null;

                try {
                    Map map = objectMapper.readValue(buff, HashMap.class);
                    message = MessageUtil.convertToIMessage(map, msgFactory, TCPIPMessageHelper.INCOMING_MESSAGE_NAME_AND_NAMESPACE,
                            TCPIPMessageHelper.INCOMING_MESSAGE_NAME_AND_NAMESPACE);

                } catch (Exception e) {
                    message = msgFactory.createMessage("Exception",
                            TCPIPMessageHelper.INCOMING_MESSAGE_NAME_AND_NAMESPACE);

                    message.addField("Cause", e.getMessage());
                }

                message.getMetaData().setRawMessage(rawData);
                out.write(message);
            } else {
                in.reset();
            }
        }

        return decoded;
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy