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

org.yamcs.api.artemis.YamcsClient 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.UUID;

import org.apache.activemq.artemis.api.core.ActiveMQException;
import org.apache.activemq.artemis.api.core.SimpleString;
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.yamcs.YamcsException;
import org.yamcs.api.YamcsApiException;
import org.yamcs.protobuf.Yamcs.ProtoDataType;
import org.yamcs.protobuf.Yamcs.StringMessage;

import com.google.protobuf.MessageLite;

import static org.yamcs.api.artemis.Protocol.*;

/**
 * Collect here scenarios and corresponding methods for sending/receiving data:
 *  sendRequest via rpcProducer 
 *     - "method" name as property and parameters as a ProtoBuf message
 *     - should automatically encode the replyTo address
 *  sendReply via rpcProducer
 *     - OK/NOK as property and return parameters as a ProtoBuf message
 *     - should somehow correlate with the request TODO
 *  
 *  executeRpc via rpcProducer/rpcConsumer
 *     -sendRequest and wait for replay a configurable timeout
 *  
 *  sendData via dataProducer
 *     - one chunk in a stream of data.
 *     - should manage end of stream conditions
 *     - should manage multiplexing different ProtoBuf message types
 *      
 *  sendEvent via dataProducer 
 *     - event name as property and parameters as ProtoBuf message
 *     
 *     
 * @author nm
 *
 */
public class YamcsClient {

    public final static String DATA_ADDRESS_PREFIX="tempDataAddress.";
    public final static String DATA_QUEUE_PREFIX="tempDataQueue.";

    public final static String RPC_ADDRESS_PREFIX="tempRpcAddress.";
    public final static String RPC_QUEUE_PREFIX="tempRpcQueue.";

    public ClientConsumer dataConsumer;
    public SimpleString dataQueue;
    public SimpleString dataAddress;


    public SimpleString rpcAddress; //this is my own address, messages are received via the consumer
    public SimpleString rpcQueue;
    public ClientConsumer rpcConsumer;
    public ClientProducer rpcProducer;

    private ClientProducer dataProducer;

    public long rpcTimeout=10000;
    long dataTimeout=10000;

    private final YamcsSession yamcsSession;


    YamcsClient(YamcsSession yamcsSession) {
        this.yamcsSession=yamcsSession;
    }

    public void sendErrorReply(SimpleString replyto, String message) throws YamcsApiException {
        synchronized(yamcsSession) {
            ClientMessage replyMsg = yamcsSession.session.createMessage(false);
            replyMsg.putStringProperty(MSG_TYPE_HEADER_NAME, "ERROR");
            replyMsg.putStringProperty(ERROR_MSG_HEADER_NAME, message);
           
            try {
                rpcProducer.send(replyto, replyMsg);
            } catch (ActiveMQException e1) {
                throw new YamcsApiException("Failed to send message to "+replyto, e1);
            }
            
        }
    }

    /**
     * Sends a ActiveMQ message containing an YamcsException.
     * The exception type and string message are encoded in the headers while the extra payload (if any) is encoded in the body
     * 
     * The exception extra arguments are encoded as 
     * @param replyto
     * @param e
     * @throws YamcsApiException
     */
    public void sendErrorReply(SimpleString replyto, YamcsException e) throws YamcsApiException {
        synchronized(yamcsSession) {
            ClientMessage replyMsg = yamcsSession.session.createMessage(false);
            replyMsg.putStringProperty(MSG_TYPE_HEADER_NAME, "ERROR");
            replyMsg.putStringProperty(ERROR_MSG_HEADER_NAME, e.getMessage());

            String type=e.getType();
            if(type!=null) {
                replyMsg.putStringProperty(ERROR_TYPE_HEADER_NAME, type);
            }

            byte[] body=e.getExtra();
            if(body!=null) {
                replyMsg.getBodyBuffer().writeBytes(body);;
            }

            try {
                rpcProducer.send(replyto, replyMsg);
            } catch (ActiveMQException e1) {
                throw new YamcsApiException("Failed to send message to "+replyto, e1);
            }
        }
    }

    public void sendReply(SimpleString replyto, String response, MessageLite body) throws YamcsApiException {
        synchronized(yamcsSession) {
            ClientMessage replyMsg = yamcsSession.session.createMessage(false);
            replyMsg.putStringProperty(MSG_TYPE_HEADER_NAME, response);
            if(body!=null) {
                encode(replyMsg, body);
            }
            try {
                rpcProducer.send(replyto, replyMsg);
            } catch (ActiveMQException e) {
                throw new YamcsApiException("Cannot send data to "+replyto, e);
            }
        }
    }



    public void sendRequest(SimpleString toAddress, String request, MessageLite body) throws YamcsApiException {
        synchronized(yamcsSession) {
            ClientMessage msg=yamcsSession.session.createMessage(false);
            msg.putStringProperty(REPLYTO_HEADER_NAME, rpcAddress);
            if(dataAddress!=null) 
                msg.putStringProperty(DATA_TO_HEADER_NAME, dataAddress);
            msg.putStringProperty(REQUEST_TYPE_HEADER_NAME, request);
            if(body!=null)  {
                encode(msg,body);
            }
            try {
                rpcProducer.send(toAddress, msg);
            } catch (ActiveMQException e) {
                throw new YamcsApiException("Cannot send data to "+toAddress, e);
            }
        }
    }


    public MessageLite executeRpc(SimpleString toAddress, String request, MessageLite body, MessageLite.Builder responseBuilder) throws YamcsApiException, YamcsException {
        synchronized(yamcsSession) {
            try {
                sendRequest(toAddress, request, body);
                ClientMessage msg = rpcConsumer.receive(rpcTimeout);
                if(msg==null) {
                    throw new YamcsApiException("did not receive a response to "+request+" in "+rpcTimeout+" milliseconds");
                }
                String resp=msg.getStringProperty(MSG_TYPE_HEADER_NAME);
                if("ERROR".equals(resp)) {
                    String type=null;
                    if(msg.containsProperty(ERROR_TYPE_HEADER_NAME)) {
                        type=msg.getStringProperty(ERROR_TYPE_HEADER_NAME);
                    }
                    String errormsg=msg.getStringProperty(ERROR_MSG_HEADER_NAME);
                    int size=msg.getBodySize();
                    byte[] extra=null;
                    if(size>0) {
                        extra=new byte[size];
                        msg.getBodyBuffer().readBytes(extra);
                    }
                    throw new YamcsException(type, errormsg, extra);
                }
                if(responseBuilder==null) {
                    return null;
                }
                else return decode(msg, responseBuilder);
            } catch (ActiveMQException e) {
                throw new YamcsApiException(e.getMessage(), e);
            }
        }
    }

    public void close() throws ActiveMQException {
        if(rpcProducer!=null) {
            rpcProducer.close();
        }
        if(rpcConsumer!=null) {
            rpcConsumer.close();
        }
        if(dataConsumer!=null) {
            dataConsumer.close();
        }
        if(dataProducer!=null) {
            dataProducer.close();
        }
    }

    public void sendDataError(SimpleString toAddress, String message) throws IOException, YamcsApiException {
        synchronized(yamcsSession) {
            sendData(toAddress, ProtoDataType.DT_ERROR, StringMessage.newBuilder().setMessage(message).build());
        }
    }

    public void sendData(SimpleString toAddress, org.yamcs.protobuf.Yamcs.ProtoDataType type, MessageLite data) throws YamcsApiException {
        synchronized(yamcsSession) {
            ClientMessage msg=yamcsSession.session.createMessage(false);
            msg.putIntProperty(DATA_TYPE_HEADER_NAME, type.getNumber());
            if(data!=null){
                encode(msg,data);
            }
            try {
                dataProducer.send(toAddress, msg);
            } catch (ActiveMQException e) {
                throw new YamcsApiException("Cannot send data to "+toAddress, e);
            }
        }
    }

    public void sendDataEnd(SimpleString toAddress) throws YamcsApiException {
        synchronized(yamcsSession) {
            ClientMessage msg=yamcsSession.session.createMessage(false);
            msg.putIntProperty(DATA_TYPE_HEADER_NAME, ProtoDataType.STATE_CHANGE.getNumber());
            try {
                dataProducer.send(toAddress,msg);
            } catch (ActiveMQException e) {
                throw new YamcsApiException("Failed to send data to "+toAddress, e);
            }
        }
    }
    /**
     * Receives and decodes data
     *  Returns null if the FINISH message is received
     */
    public MessageLite receiveData(MessageLite.Builder dataBuilder) throws YamcsException, YamcsApiException, ActiveMQException {
        ClientMessage msg = dataConsumer.receive(dataTimeout);
        if(msg==null) {
            throw new YamcsException("did not received a data message, timeout"+dataTimeout);
        }
        int dt=msg.getIntProperty(DATA_TYPE_HEADER_NAME);
        if(dt==ProtoDataType.STATE_CHANGE.getNumber()){
            return null;
        } else if(dt==ProtoDataType.DT_ERROR.getNumber()) {
            StringMessage errormsg=(StringMessage) decode(msg, StringMessage.newBuilder());
            throw new YamcsException(errormsg.getMessage());
        }
        return decode(msg,dataBuilder);
    }


    public MessageLite receiveImmediate(MessageLite.Builder dataBuilder) throws YamcsException, YamcsApiException {
        ClientMessage msg;
        try {
            msg = dataConsumer.receive(dataTimeout);
        } catch (ActiveMQException e) {
            throw new YamcsApiException("Exception receiving data", e);
        }
        if(msg==null) {
            return null;
        }
        int dt = msg.getIntProperty(DATA_TYPE_HEADER_NAME);
        if(dt==ProtoDataType.STATE_CHANGE.getNumber()){
            return null;
        } else if(dt==ProtoDataType.DT_ERROR.getNumber()) {
            StringMessage errormsg=(StringMessage) decode(msg, StringMessage.newBuilder());
            throw new YamcsException(errormsg.getMessage());
        }
        return decode(msg,dataBuilder);
    }


    /**
     * sends an event via the dataProducer
     * @param toAddress
     * @param eventName name of the event to be encoded in the {@value Protocol#HDR_EVENT_NAME} header
     * @param data
     * @throws ActiveMQException
     */
    public void sendEvent(SimpleString toAddress, String eventName, MessageLite data) throws ActiveMQException {
        synchronized(yamcsSession) {
            ClientMessage msg=yamcsSession.session.createMessage(false);
            msg.putStringProperty(HDR_EVENT_NAME, eventName);
            if(data!=null){
                encode(msg,data);
            }
            dataProducer.send(toAddress, msg);
        }
    }


    public YamcsSession getYamcsSession() {
        return yamcsSession;
    }


    public static class ClientBuilder {
        boolean invm=true;

        boolean dataProducer=false;
        boolean dataConsumer=false;
        boolean rpc=false;


        SimpleString rpcAddress;
        SimpleString rpcQueue;

        SimpleString dataAddress;
        SimpleString dataQueue;

        SimpleString filter=null;

        final YamcsSession yamcsSession;

        private boolean browseOnly=false;

        public ClientBuilder(YamcsSession yamcsSession) {
            if(yamcsSession.session==null) {
                throw new IllegalArgumentException();
            }
            this.yamcsSession=yamcsSession;
        }

        /**
         * mark this as a rpc client
         * @param rpc
         * @return
         */
        public ClientBuilder setRpc(boolean rpc) {
            this.rpc=rpc;
            return this;
        }

        /**
         * mark this as a rpc server and set the rpc address
         * @param address
         * @return
         */
        public ClientBuilder setRpcAddress(SimpleString address) {
            this.rpc=true;
            this.rpcAddress=address;
            return this;
        }

        /**
         * Create a data producer. The address to send to is specified when sending each message
         * @param b
         * @return
         */
        public ClientBuilder setDataProducer(boolean b) {
            dataProducer=b;
            return this;
        }

        /**
         * Create a data consumer from the specified address and queue. 
         *  Both can be null in which case a temporary address and/or a temporary queue are created,
         * @param address
         * @param queue
         * @return
         */
        public ClientBuilder setDataConsumer(SimpleString address, SimpleString queue) {
            this.dataConsumer=true;
            this.dataAddress = address;
            this.dataQueue = queue;
            return this;
        }

        /**
         * Sets a filter for the data consumer
         * @param filter
         * @return
         */
        public ClientBuilder setFilter(SimpleString filter) {
            this.filter=filter;
            return this;
        }
        /**
         * Sets the data consumer browseOnly property
         * @param b
         * @return
         */
        public ClientBuilder setBrowseOnly(boolean b) {
            this.browseOnly=b;
            return this;
        }

        public YamcsClient build() throws YamcsApiException {
            try {
                YamcsClient c=new YamcsClient(yamcsSession);
                if(rpc) {
                    c.rpcAddress = (rpcAddress==null)?new SimpleString(RPC_ADDRESS_PREFIX + UUID.randomUUID().toString()):rpcAddress;
                    c.rpcQueue = (rpcQueue==null)?new SimpleString(RPC_QUEUE_PREFIX + UUID.randomUUID().toString()):rpcQueue;
                    createAddressAndQueue(yamcsSession.session, c.rpcAddress, c.rpcQueue, filter);
                    c.rpcConsumer = yamcsSession.session.createConsumer(c.rpcQueue);
                    c.rpcProducer = yamcsSession.session.createProducer();
                } 

                if(dataConsumer) {
                    c.dataAddress = (dataAddress==null)?new SimpleString(DATA_ADDRESS_PREFIX + UUID.randomUUID().toString()):dataAddress;
                    c.dataQueue = (dataQueue==null)?new SimpleString(DATA_QUEUE_PREFIX + UUID.randomUUID().toString()):dataQueue;
                    createAddressAndQueue(yamcsSession.session, c.dataAddress, c.dataQueue, filter);
                    c.dataConsumer = yamcsSession.session.createConsumer(c.dataQueue, browseOnly);
                }

                if(dataProducer) {
                    c.dataProducer = yamcsSession.session.createProducer();
                }

                return c;
            } catch (ActiveMQException e) {
                throw new YamcsApiException("cannot create yamcs client: "+e.getMessage(), e);
            }

        }

        static void createAddressAndQueue(ClientSession session, SimpleString a, SimpleString q, SimpleString filter) throws ActiveMQException {
            if(!session.queueQuery(q).isExists()) {
                if(filter==null) {
                    session.createTemporaryQueue(a, q);
                } else {
                    session.createTemporaryQueue(a, q, filter);
                }
            }
        }


    }

    /**
     * Send message to the address using the data producer.
     *      
     * @param hornetAddress
     * @param msg
     * @throws YamcsApiException
     */
    public synchronized void sendData(SimpleString hornetAddress, ClientMessage msg) throws YamcsApiException {
        synchronized(yamcsSession) {
            try {
                dataProducer.send(hornetAddress, msg);
            } catch (ActiveMQException e) {
               throw new YamcsApiException("Failed to send message to "+hornetAddress, e);
            }
        }
    }

    public ClientProducer getDataProducer() {
        return dataProducer;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy