org.yamcs.api.artemis.Protocol Maven / Gradle / Ivy
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.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.ActiveMQClient;
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.api.YamcsApiException;
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 YamcsApiException {
try {
ActiveMQBuffer buf = msg.getBodyBuffer();
return builder.mergeFrom(new ActiveMQBufferInputStream(buf)).build();
} catch(IOException e) {
throw new YamcsApiException(e.getMessage(), e);
}
}
public static MessageLite.Builder decodeBuilder(ClientMessage msg, MessageLite.Builder builder) throws YamcsApiException {
try {
ActiveMQBuffer buf = msg.getBodyBuffer();
return builder.mergeFrom(new ActiveMQBufferInputStream(buf));
} catch(IOException e) {
throw new YamcsApiException(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;
ProducerKiller() throws Exception {
locator = ActiveMQClient.createServerLocatorWithoutHA(new TransportConfiguration(IN_VM_FACTORY));
ClientSessionFactory 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");
ClientConsumer 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