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

org.yamcs.api.artemis.Protocol Maven / Gradle / Ivy

There is a newer version: 4.10.9
Show newest version
package org.yamcs.api.artemis;

import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.activemq.artemis.api.core.ActiveMQBuffer;
import org.apache.activemq.artemis.api.core.ActiveMQException;
import org.apache.activemq.artemis.api.core.SimpleString;
import org.apache.activemq.artemis.api.core.TransportConfiguration;
import org.apache.activemq.artemis.api.core.client.ActiveMQClient;
import org.apache.activemq.artemis.api.core.client.ClientConsumer;
import org.apache.activemq.artemis.api.core.client.ClientMessage;
import org.apache.activemq.artemis.api.core.client.ClientProducer;
import org.apache.activemq.artemis.api.core.client.ClientSession;
import org.apache.activemq.artemis.api.core.client.ClientSessionFactory;
import org.apache.activemq.artemis.api.core.client.MessageHandler;
import org.apache.activemq.artemis.api.core.client.ServerLocator;
import org.apache.activemq.artemis.utils.ActiveMQBufferInputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.yamcs.protobuf.Yamcs.ProtoDataType;
import org.yamcs.utils.ActiveMQBufferOutputStream;

import com.google.protobuf.MessageLite;

public class Protocol {
    static Logger log = LoggerFactory.getLogger(Protocol.class.getName());

    private static ProducerKiller killer;

    public static SimpleString getReplayControlAddress(String instance) {
        return new SimpleString(instance + ".replayControl");
    }

    public static SimpleString getYarchRetrievalControlAddress(String instance) {
        return new SimpleString(instance + ".retrievalControl");
    }

    public static SimpleString getYarchIndexControlAddress(String instance) {
        return new SimpleString(instance + ".indexControl");
    }

    public static SimpleString getEventRealtimeAddress(String instance) {
        return new SimpleString(instance + ".events_realtime");
    }

    public static SimpleString getParameterRealtimeAddress(String instance) {
        return new SimpleString(instance + ".parameters_realtime");
    }

    public static SimpleString getPacketRealtimeAddress(String instance) {
        return new SimpleString(instance + ".tm_realtime");
    }

    public static SimpleString getPacketAddress(String instance, String streamName) {
        return new SimpleString(instance + "." + streamName);
    }

    final static public String MSG_TYPE_HEADER_NAME = "yamcs-content";

    final static public String REQUEST_TYPE_HEADER_NAME = "yamcs-req-type";

    final static public String ERROR_MSG_HEADER_NAME = "yamcs-error-msg";
    final static public String ERROR_TYPE_HEADER_NAME = "yamcs-error-type";

    final static public SimpleString DATA_TO_HEADER_NAME = new SimpleString("yamcs-data-to");
    final static public SimpleString YAMCS_SERVER_CONTROL_ADDRESS = new SimpleString("yamcsControl");
    final static public SimpleString REPLYTO_HEADER_NAME = new SimpleString("yamcs-reply-to");

    final public static String IN_VM_FACTORY = "org.apache.activemq.artemis.core.remoting.impl.invm.InVMConnectorFactory";

    /**
     * used to send chunks of data in a stream
     */
    final static public String DATA_TYPE_HEADER_NAME = "dt";

    /**
     * used to send one shot events (publish/subscribe)
     */
    final static public String HDR_EVENT_NAME = "en";

    /**
     * address where all the I/O Links are registered (e.g. tmprovide and tcuplinker) there is also a queue with the
     * same name that can be browsed to get the initial list
     */
    final static public SimpleString LINK_INFO_ADDRESS = new SimpleString("linkInfo");
    /**
     * address where link control messages can be sent
     */
    final static public SimpleString LINK_CONTROL_ADDRESS = new SimpleString("linkControl");

    /**
     * address where all the Channels are registered there is also a queue with the same name that can be browsed to get
     * the initial list
     */
    final static public SimpleString YPROCESSOR_INFO_ADDRESS = new SimpleString("channelInfo");

    /**
     * address where channel control commands can be sent
     */
    final static public SimpleString YPROCESSOR_CONTROL_ADDRESS = new SimpleString("channelControl");

    /**
     * address where channel statistics are published regularly
     */
    final static public SimpleString YPROCESSOR_STATISTICS_ADDRESS = new SimpleString("channelStatistics");

    /**
     * address where all the Command Queues and command stationed in the Queues are registered there is also a queue
     * with the same name that can be browsed to get the initial list
     */
    final static public SimpleString CMDQUEUE_INFO_ADDRESS = new SimpleString("cmdQueueInfo");

    /**
     * address where command queue control commands can be sent
     */
    final static public SimpleString CMDQUEUE_CONTROL_ADDRESS = new SimpleString("cmdQueueControl");

    public static MessageLite decode(ClientMessage msg, MessageLite.Builder builder) throws ArtemisApiException {
        try {
            ActiveMQBuffer buf = msg.getBodyBuffer();
            return builder.mergeFrom(new ActiveMQBufferInputStream(buf)).build();
        } catch (IOException e) {
            throw new ArtemisApiException(e.getMessage(), e);
        }
    }

    public static MessageLite.Builder decodeBuilder(ClientMessage msg, MessageLite.Builder builder)
            throws ArtemisApiException {
        try {
            ActiveMQBuffer buf = msg.getBodyBuffer();
            return builder.mergeFrom(new ActiveMQBufferInputStream(buf));
        } catch (IOException e) {
            throw new ArtemisApiException(e.getMessage(), e);
        }
    }

    public static void encode(ClientMessage msg, MessageLite ml) {
        try {
            ml.writeTo(new ActiveMQBufferOutputStream(msg.getBodyBuffer()));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static boolean endOfStream(ClientMessage msg) {
        int type = msg.getIntProperty(DATA_TYPE_HEADER_NAME);
        return (type == ProtoDataType.STATE_CHANGE.getNumber());
    }

    /**
     * Closes producer when a consumer for address a has been detected as dead
     * 
     * @param p
     * @param a
     */
    public static synchronized void killProducerOnConsumerClosed(ClientProducer p, SimpleString a) {
        if ((killer == null) || killer.session.isClosed()) {// killer.session gets closed when the artemis is stopped
                                                            // (during test execution by maven)
            try {
                killer = new ProducerKiller();
            } catch (Exception e) {
                log.error("Could not create ProducerKiller", e);
            }
        }
        if (killer != null) {
            killer.add(p, a);
        }
    }

    /**
     * this is supposed to close the data producers that are stuck writing into queues for which there is no consumer
     * (e.g. when the consumer loses the network connection to the server)
     *
     */
    static class ProducerKiller implements MessageHandler {
        ClientSession session;
        Map producers = new ConcurrentHashMap<>();
        ServerLocator locator;
        ClientSessionFactory factory;
        ClientConsumer consumer ;
        
        ProducerKiller() throws Exception {
            locator = ActiveMQClient.createServerLocatorWithoutHA(new TransportConfiguration(IN_VM_FACTORY));
            factory = locator.createSessionFactory();

            session = factory.createSession(YamcsSession.hornetqInvmUser, YamcsSession.hornetqInvmPass, false, true,
                    true, true, 1);

            // session.createQueue("example", "example", true);
            session.createTemporaryQueue("activemq.notifications", "ProducerKiller");
            consumer = session.createConsumer("ProducerKiller");
            consumer.setMessageHandler(this);
            session.start();
        }

        public void add(ClientProducer p, SimpleString a) {
            producers.put(a, p);
        }

        @Override
        public void onMessage(ClientMessage msg) {
            String amq_notifType = msg.getStringProperty("_AMQ_NotifType");

            if ("CONSUMER_CLOSED".equals(amq_notifType)) {
                SimpleString amq_address = msg.getSimpleStringProperty("_AMQ_Address");
                ClientProducer p = producers.remove(amq_address);
                if (p != null && !p.isClosed()) {
                    try {
                        log.warn("closing producer {} because the consumer to the address {} has closed", p,
                                amq_address);
                        p.close();
                    } catch (ActiveMQException e) {
                        e.printStackTrace();
                    }
                }
            }

        }

        public void close() {
            if (locator != null) {
                locator.close();
            }
        }
    }

    /**
     * Close killer (used in order to reduce warnings during maven builds
     */
    public static synchronized void closeKiller() {
        if (killer != null) {
            killer.close();
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy