package gov.nasa.jpl.mbee.mdk.mms.jms;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.nomagic.magicdraw.core.Application;
import com.nomagic.magicdraw.core.Project;
import gov.nasa.jpl.mbee.mdk.api.incubating.convert.Converters;
import gov.nasa.jpl.mbee.mdk.http.ServerException;
import gov.nasa.jpl.mbee.mdk.json.JacksonUtils;
import gov.nasa.jpl.mbee.mdk.mms.MMSUtils;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.http.client.utils.URIBuilder;
import javax.jms.*;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.Hashtable;
/**
* Created by igomes on 6/29/16.
*/
public class JMSUtils {
private static final int JMS_PORT = 61616;
private static final String JMS_S = "tcp";
public static final String MSG_SELECTOR_PROJECT_ID = "projectId",
MSG_SELECTOR_REF_ID = "refId";
// Members to look up MMS using JNDI
// TODO: If any other context factories are used, need to add those JARs into class path (e.g., for weblogic)
public static String JMS_CTX_FACTORY = "org.apache.activemq.jndi.ActiveMQInitialContextFactory",
JMS_CONN_FACTORY = "ConnectionFactory",
JMS_USERNAME,
JMS_PASSWORD,
JMS_TOPIC = "master";
private static InitialContext initialContext = null;
/**
* Gets MMS JNDI connection details from the MMS server
*
* @return JSONObject of the connection details
* @throws JsonMappingException
* @throws IOException
* @throws JsonParseException
*/
public static ObjectNode getJmsConnectionDetails(Project project)
throws IOException, ServerException, URISyntaxException {
ObjectNode responseJson = JacksonUtils.getObjectMapper().createObjectNode();
URIBuilder requestUri = MMSUtils.getServiceUri(project);
if (requestUri == null) {
return responseJson;
}
requestUri.setPath(requestUri.getPath() + "/connection/jms");
MMSUtils.sendMMSRequest(project, MMSUtils.buildRequest(MMSUtils.HttpRequestType.GET, requestUri), null, responseJson);
// File responseFile = MMSUtils.sendMMSRequest(project, MMSUtils.buildRequest(MMSUtils.HttpRequestType.GET, requestUri));
// try (JsonParser jsonParser = JacksonUtils.getJsonFactory().createParser(responseFile)) {
// return JacksonUtils.parseJsonObject(jsonParser);
// }
return responseJson;
}
// Varies by current project
public static JMSInfo getJMSInfo(Project project) throws ServerException {
ObjectNode jmsJson = null;
try {
jmsJson = getJmsConnectionDetails(project);
} catch (IOException e) {
Application.getInstance().getGUILog().log("[ERROR]: Unable to acquire JMS Connection information.");
e.printStackTrace();
} catch (URISyntaxException e) {
Application.getInstance().getGUILog().log("[ERROR]: Unexpected error occurred when trying to build MMS URL. Reason: " + e.getMessage());
e.printStackTrace();
}
String url = ingestJson(jmsJson);
boolean isFromService = (url != null);
if (url == null) {
url = MMSUtils.getServerUrl(project);
try {
URIBuilder uri = new URIBuilder(url);
uri.setPort(JMS_PORT);
uri.setScheme(JMS_S);
uri.setPath("");
url = uri.build().toString();
} catch (URISyntaxException e) {
e.printStackTrace();
}
}
return new JMSInfo(url, isFromService);
}
/**
* Ingests JSON data generated from MMS server and populates JNDI members
*
* @return URL string of connector
*/
protected static String ingestJson(ObjectNode jsonInput) {
if (jsonInput == null) {
return null;
}
ArrayNode conns = null;
JsonNode valueNode;
if ((valueNode = jsonInput.get("connections")) != null && valueNode.isArray()) {
conns = (ArrayNode) valueNode;
}
ObjectNode json = null;
if (conns != null) {
// just grab first connection
for (int ii = 0; ii < conns.size(); ii++) {
json = (ObjectNode) conns.get(ii);
JsonNode value;
if ((value = json.get("eventType")) != null && value.isTextual() && value.asText().equals("DELTA")) {
break;
}
}
}
else {
json = jsonInput;
}
String result = null;
if (json != null) {
if ((valueNode = json.get("uri")) != null && valueNode.isTextual()) {
result = valueNode.asText();
}
if ((valueNode = json.get("connFactory")) != null && valueNode.isTextual()) {
JMS_CONN_FACTORY = valueNode.asText();
}
if ((valueNode = json.get("ctxFactory")) != null && valueNode.isTextual()) {
JMS_CTX_FACTORY = valueNode.asText();
}
if ((valueNode = json.get("password")) != null && valueNode.isTextual()) {
JMS_PASSWORD = valueNode.asText();
}
if ((valueNode = json.get("username")) != null && valueNode.isTextual()) {
JMS_USERNAME = valueNode.asText();
}
if ((valueNode = json.get("topicName")) != null && valueNode.isTextual()) {
JMS_TOPIC = valueNode.asText();
}
}
return result;
}
public static InitialContext getInitialContext() {
return initialContext;
}
/**
* Create a connection factory based on JNDI values
*
* @return
*/
public static ConnectionFactory createConnectionFactory(JMSInfo jmsInfo) {
boolean isFromService = jmsInfo.isFromService();
String url = jmsInfo.getUrl();
Hashtable<String, String> properties = new Hashtable<>();
properties.put(Context.INITIAL_CONTEXT_FACTORY, JMS_CTX_FACTORY);
properties.put(Context.PROVIDER_URL, url);
if (JMS_USERNAME != null && JMS_PASSWORD != null) {
properties.put(Context.SECURITY_PRINCIPAL, JMS_USERNAME);
properties.put(Context.SECURITY_CREDENTIALS, JMS_PASSWORD);
}
initialContext = null;
try {
initialContext = new InitialContext(properties);
} catch (NamingException ne) {
// FIXME: getting java.lang.ClassNotFoundException: org.apache.activemq.jndi.ActiveMQInitialContextFactory
// works in debugging from Eclipse - somehow classpath doesn't work
// plugin has the activemq-all reference, as workaround set to false for now
isFromService = false;
}
if (!isFromService) {
return new ActiveMQConnectionFactory(url);
}
else {
try {
return (ConnectionFactory) initialContext.lookup(JMS_CONN_FACTORY);
} catch (NamingException ne) {
ne.printStackTrace(System.err);
return null;
}
}
}
public static String constructSelectorString(String projectID, String workspaceID) {
StringBuilder selectorBuilder = new StringBuilder();
//selectorBuilder.append("(").append(MSG_SELECTOR_REF_ID).append("='").append(workspaceID).append("')");
selectorBuilder.append("(").append(MSG_SELECTOR_PROJECT_ID).append(" = '").append(projectID).append("')")
.append(" AND ").append("((").append(MSG_SELECTOR_REF_ID).append(" = '").append(workspaceID).append("') OR (").append(MSG_SELECTOR_REF_ID).append(" = '").append(workspaceID).append("_mdk").append("'))");
String outputMsgSelector = selectorBuilder.toString();
selectorBuilder.delete(0, selectorBuilder.length());
return outputMsgSelector;
}
public static void initializeDurableQueue(Project project, String workspace) {
String projectId = Converters.getIProjectToIdConverter().apply(project.getPrimaryProject());
Connection connection = null;
Session session = null;
MessageConsumer consumer = null;
try {
JMSUtils.JMSInfo jmsInfo = null;
try {
jmsInfo = JMSUtils.getJMSInfo(project);
} catch (ServerException e) {
e.printStackTrace();
}
String url = jmsInfo != null ? jmsInfo.getUrl() : null;
if (url == null) {
return;
}
ConnectionFactory connectionFactory = JMSUtils.createConnectionFactory(jmsInfo);
if (connectionFactory == null) {
return;
}
connection = connectionFactory.createConnection();
String subscriberId = projectId + "/" + workspace;
connection.setClientID(subscriberId);
// connection.setExceptionListener(this);
session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE);
String messageSelector = JMSUtils.constructSelectorString(projectId, workspace);
Topic topic = session.createTopic("master");
consumer = session.createDurableSubscriber(topic, subscriberId, messageSelector, true);
connection.start();
} catch (JMSException e1) {
e1.printStackTrace();
} finally {
try {
if (consumer != null) {
consumer.close();
}
if (session != null) {
session.close();
}
if (connection != null) {
connection.close();
}
} catch (JMSException e) {
e.printStackTrace();
}
}
}
public static class JMSInfo {
private final String url;
private final boolean isFromService;
public JMSInfo(String url, boolean isFromService) {
this.url = url;
this.isFromService = isFromService;
}
public String getUrl() {
return url;
}
public boolean isFromService() {
return isFromService;
}
}
}