org.yamcs.api.artemis.YamcsClient Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of yamcs-artemis Show documentation
Show all versions of yamcs-artemis Show documentation
Yamcs Artemis senders/receivers
package org.yamcs.api.artemis;
import static org.yamcs.api.artemis.Protocol.DATA_TO_HEADER_NAME;
import static org.yamcs.api.artemis.Protocol.DATA_TYPE_HEADER_NAME;
import static org.yamcs.api.artemis.Protocol.ERROR_MSG_HEADER_NAME;
import static org.yamcs.api.artemis.Protocol.ERROR_TYPE_HEADER_NAME;
import static org.yamcs.api.artemis.Protocol.HDR_EVENT_NAME;
import static org.yamcs.api.artemis.Protocol.MSG_TYPE_HEADER_NAME;
import static org.yamcs.api.artemis.Protocol.REPLYTO_HEADER_NAME;
import static org.yamcs.api.artemis.Protocol.REQUEST_TYPE_HEADER_NAME;
import static org.yamcs.api.artemis.Protocol.decode;
import static org.yamcs.api.artemis.Protocol.encode;
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.protobuf.Yamcs.ProtoDataType;
import org.yamcs.protobuf.Yamcs.StringMessage;
import com.google.protobuf.MessageLite;
/**
* 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 ArtemisApiException {
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 ArtemisApiException("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 ArtemisApiException
*/
public void sendErrorReply(SimpleString replyto, YamcsException e) throws ArtemisApiException {
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 ArtemisApiException("Failed to send message to " + replyto, e1);
}
}
}
public void sendReply(SimpleString replyto, String response, MessageLite body) throws ArtemisApiException {
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 ArtemisApiException("Cannot send data to " + replyto, e);
}
}
}
public void sendRequest(SimpleString toAddress, String request, MessageLite body) throws ArtemisApiException {
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 ArtemisApiException("Cannot send data to " + toAddress, e);
}
}
}
public MessageLite executeRpc(SimpleString toAddress, String request, MessageLite body,
MessageLite.Builder responseBuilder) throws ArtemisApiException, YamcsException {
synchronized (yamcsSession) {
try {
sendRequest(toAddress, request, body);
ClientMessage msg = rpcConsumer.receive(rpcTimeout);
if (msg == null) {
throw new ArtemisApiException(
"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 ArtemisApiException(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, ArtemisApiException {
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 ArtemisApiException {
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 ArtemisApiException("Cannot send data to " + toAddress, e);
}
}
}
public void sendDataEnd(SimpleString toAddress) throws ArtemisApiException {
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 ArtemisApiException("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, ArtemisApiException, 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, ArtemisApiException {
ClientMessage msg;
try {
msg = dataConsumer.receive(dataTimeout);
} catch (ActiveMQException e) {
throw new ArtemisApiException("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 ArtemisApiException {
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 ArtemisApiException("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 ArtemisApiException
*/
public synchronized void sendData(SimpleString hornetAddress, ClientMessage msg) throws ArtemisApiException {
synchronized (yamcsSession) {
try {
dataProducer.send(hornetAddress, msg);
} catch (ActiveMQException e) {
throw new ArtemisApiException("Failed to send message to " + hornetAddress, e);
}
}
}
public ClientProducer getDataProducer() {
return dataProducer;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy