package io.lumify.core.model.workQueue;
import com.altamiracorp.bigtable.model.FlushFlag;
import com.google.inject.Inject;
import io.lumify.core.config.Configuration;
import io.lumify.core.exception.LumifyException;
import io.lumify.core.ingest.WorkerSpout;
import io.lumify.core.model.notification.SystemNotification;
import io.lumify.core.model.notification.SystemNotificationRepository;
import io.lumify.core.model.notification.UserNotification;
import io.lumify.core.model.notification.UserNotificationRepository;
import io.lumify.core.model.user.UserRepository;
import io.lumify.core.user.User;
import io.lumify.core.util.ClientApiConverter;
import io.lumify.core.util.LumifyLogger;
import io.lumify.core.util.LumifyLoggerFactory;
import io.lumify.web.clientapi.model.ClientApiWorkspace;
import io.lumify.web.clientapi.model.UserStatus;
import org.json.JSONArray;
import org.json.JSONObject;
import org.securegraph.*;
import java.util.List;
import java.util.Map;
import static com.google.common.base.Preconditions.checkNotNull;
public abstract class WorkQueueRepository {
protected static final LumifyLogger LOGGER = LumifyLoggerFactory.getLogger(WorkQueueRepository.class);
public static String GRAPH_PROPERTY_QUEUE_NAME = "graphProperty";
public static String LONG_RUNNING_PROCESS_QUEUE_NAME = "longRunningProcess";
private final Graph graph;
@Inject
protected WorkQueueRepository(Graph graph, Configuration configuration) {
this.graph = graph;
String prefix = configuration.get(Configuration.QUEUE_PREFIX, null);
if (prefix != null) {
GRAPH_PROPERTY_QUEUE_NAME = prefix + "-" + GRAPH_PROPERTY_QUEUE_NAME;
LONG_RUNNING_PROCESS_QUEUE_NAME = prefix + "-" + LONG_RUNNING_PROCESS_QUEUE_NAME;
}
}
public void pushGraphPropertyQueue(final Element element, final Property property) {
pushGraphPropertyQueue(element, property.getKey(), property.getName());
}
public void pushGraphPropertyQueue(final Element element, final Property property, String workspaceId, String visibilitySource) {
pushGraphPropertyQueue(element, property.getKey(), property.getName(), workspaceId, visibilitySource);
}
public void pushElementImageQueue(final Element element, final Property property) {
pushElementImageQueue(element, property.getKey(), property.getName());
}
public void pushElementImageQueue(final Element element, String propertyKey, final String propertyName) {
getGraph().flush();
checkNotNull(element);
JSONObject data = new JSONObject();
if (element instanceof Vertex) {
data.put("graphVertexId", element.getId());
} else if (element instanceof Edge) {
data.put("graphEdgeId", element.getId());
} else {
throw new LumifyException("Unexpected element type: " + element.getClass().getName());
}
data.put("propertyKey", propertyKey);
data.put("propertyName", propertyName);
pushOnQueue(GRAPH_PROPERTY_QUEUE_NAME, FlushFlag.DEFAULT, data);
broadcastEntityImage(element, propertyKey, propertyName);
}
public void pushGraphPropertyQueue(final Element element, String propertyKey, final String propertyName) {
pushGraphPropertyQueue(element, propertyKey, propertyName, null, null);
}
public void pushGraphPropertyQueue(final Element element, String propertyKey, final String propertyName,
String workspaceId, String visibilitySource) {
getGraph().flush();
checkNotNull(element);
JSONObject data = new JSONObject();
if (element instanceof Vertex) {
data.put("graphVertexId", element.getId());
} else if (element instanceof Edge) {
data.put("graphEdgeId", element.getId());
} else {
throw new LumifyException("Unexpected element type: " + element.getClass().getName());
}
if (workspaceId != null && !workspaceId.equals("")) {
data.put("workspaceId", workspaceId);
data.put("visibilitySource", visibilitySource);
}
data.put("propertyKey", propertyKey);
data.put("propertyName", propertyName);
pushOnQueue(GRAPH_PROPERTY_QUEUE_NAME, FlushFlag.DEFAULT, data);
broadcastPropertyChange(element, propertyKey, propertyName, workspaceId);
}
public void pushLongRunningProcessQueue(JSONObject queueItem) {
broadcastLongRunningProcessChange(queueItem);
pushOnQueue(LONG_RUNNING_PROCESS_QUEUE_NAME, FlushFlag.DEFAULT, queueItem);
}
public void broadcastLongRunningProcessChange(JSONObject longRunningProcessQueueItem) {
String userId = longRunningProcessQueueItem.optString("userId");
checkNotNull(userId, "userId cannot be null");
JSONObject json = new JSONObject();
json.put("type", "longRunningProcessChange");
JSONObject permissions = new JSONObject();
JSONArray users = new JSONArray();
users.put(userId);
permissions.put("users", users);
json.put("permissions", permissions);
JSONObject dataJson = new JSONObject(longRunningProcessQueueItem.toString());
/// because results can get quite large we don't want this going on in a web socket message
if (dataJson.has("results")) {
dataJson.remove("results");
}
json.put("data", dataJson);
broadcastJson(json);
}
public void pushElement(Element element) {
pushGraphPropertyQueue(element, null, null);
}
public void pushEdgeDeletion(Edge edge) {
broadcastEdgeDeletion(edge);
}
protected void broadcastEdgeDeletion(Edge edge) {
JSONObject dataJson = new JSONObject();
if (edge != null) {
dataJson.put("edgeId", edge.getId());
dataJson.put("outVertexId", edge.getVertexId(Direction.OUT));
dataJson.put("inVertexId", edge.getVertexId(Direction.IN));
}
JSONObject json = new JSONObject();
json.put("type", "edgeDeletion");
json.put("data", dataJson);
broadcastJson(json);
}
public void pushVertexDeletion(Vertex vertex) {
broadcastVertexDeletion(vertex);
}
protected void broadcastVertexDeletion(Vertex vertex) {
JSONArray vertexIds = new JSONArray();
vertexIds.put(vertex.getId());
broadcastVerticesDeletion(vertexIds);
}
public void pushVerticesDeletion(JSONArray verticesDeleted) {
broadcastVerticesDeletion(verticesDeleted);
}
protected void broadcastVerticesDeletion(JSONArray verticesDeleted) {
JSONObject dataJson = new JSONObject();
if (verticesDeleted != null) {
dataJson.put("vertexIds", verticesDeleted);
}
JSONObject json = new JSONObject();
json.put("type", "verticesDeleted");
json.put("data", dataJson);
broadcastJson(json);
}
public void pushTextUpdated(String vertexId) {
broadcastTextUpdated(vertexId);
}
protected void broadcastTextUpdated(String vertexId) {
JSONObject dataJson = new JSONObject();
if (vertexId != null) {
dataJson.put("graphVertexId", vertexId);
}
JSONObject json = new JSONObject();
json.put("type", "textUpdated");
json.put("data", dataJson);
broadcastJson(json);
}
public void pushUserStatusChange(User user, UserStatus status) {
broadcastUserStatusChange(user, status);
}
protected void broadcastUserStatusChange(User user, UserStatus status) {
JSONObject json = new JSONObject();
json.put("type", "userStatusChange");
JSONObject data = UserRepository.toJson(user);
data.put("status", status.toString());
json.put("data", data);
broadcastJson(json);
}
public void pushUserCurrentWorkspaceChange(User user, String workspaceId) {
broadcastUserWorkspaceChange(user, workspaceId);
}
public void pushWorkspaceChange(ClientApiWorkspace workspace, List<ClientApiWorkspace.User> previousUsers, String changedByUserId) {
broadcastWorkspace(workspace, previousUsers, changedByUserId);
}
protected void broadcastUserWorkspaceChange(User user, String workspaceId) {
JSONObject json = new JSONObject();
json.put("type", "userWorkspaceChange");
JSONObject data = UserRepository.toJson(user);
data.put("workspaceId", workspaceId);
json.put("data", data);
broadcastJson(json);
}
protected void broadcastWorkspace(ClientApiWorkspace workspace, List<ClientApiWorkspace.User> previousUsers, String changedByUserId) {
JSONObject json = new JSONObject();
json.put("type", "workspaceChange");
json.put("modifiedBy", changedByUserId);
json.put("permissions", getPermissionsWithUsers(workspace, previousUsers));
json.put("data", new JSONObject(ClientApiConverter.clientApiToString(workspace)));
broadcastJson(json);
}
public void pushWorkspaceDelete(ClientApiWorkspace workspace) {
JSONObject json = new JSONObject();
json.put("type", "workspaceDelete");
json.put("permissions", getPermissionsWithUsers(workspace, null));
json.put("workspaceId", workspace.getWorkspaceId());
broadcastJson(json);
}
public void pushWorkspaceDelete(String workspaceId, String userId) {
JSONObject json = new JSONObject();
json.put("type", "workspaceDelete");
JSONObject permissions = new JSONObject();
JSONArray users = new JSONArray();
users.put(userId);
permissions.put("users", users);
json.put("permissions", permissions);
json.put("workspaceId", workspaceId);
broadcastJson(json);
}
private JSONObject getPermissionsWithUsers(ClientApiWorkspace workspace, List<ClientApiWorkspace.User> previousUsers) {
JSONObject permissions = new JSONObject();
JSONArray users = new JSONArray();
if (previousUsers != null) {
for (ClientApiWorkspace.User user : previousUsers) {
users.put(user.getUserId());
}
}
for (ClientApiWorkspace.User user : workspace.getUsers()) {
users.put(user.getUserId());
}
permissions.put("users", users);
return permissions;
}
public void pushSessionExpiration(String userId, String sessionId) {
JSONObject json = new JSONObject();
json.put("type", "sessionExpiration");
JSONObject permissions = new JSONObject();
JSONArray users = new JSONArray();
users.put(userId);
permissions.put("users", users);
JSONArray sessionIds = new JSONArray();
sessionIds.put(sessionId);
permissions.put("sessionIds", sessionIds);
json.put("permissions", permissions);
json.putOpt("sessionId", sessionId);
broadcastJson(json);
}
public void pushUserNotification(UserNotification notification) {
JSONObject json = new JSONObject();
json.put("type", "notification");
JSONObject permissions = new JSONObject();
JSONArray users = new JSONArray();
users.put(notification.getUserId());
permissions.put("users", users);
json.put("permissions", permissions);
JSONObject data = new JSONObject();
json.put("data", data);
data.put("notification", UserNotificationRepository.toJSONObject(notification));
broadcastJson(json);
}
public void pushSystemNotification(SystemNotification notification) {
JSONObject json = new JSONObject();
json.put("type", "notification");
JSONObject data = new JSONObject();
json.put("data", data);
data.put("notification", SystemNotificationRepository.toJSONObject(notification));
broadcastJson(json);
}
public void pushSystemNotificationUpdate(SystemNotification notification) {
JSONObject json = new JSONObject();
json.put("type", "systemNotificationUpdated");
JSONObject data = new JSONObject();
json.put("data", data);
data.put("notification", SystemNotificationRepository.toJSONObject(notification));
broadcastJson(json);
}
public void pushSystemNotificationEnded(String notificationId) {
JSONObject json = new JSONObject();
json.put("type", "systemNotificationEnded");
JSONObject data = new JSONObject();
json.put("data", data);
data.put("notificationId", notificationId);
broadcastJson(json);
}
protected void broadcastPropertyChange(Element element, String propertyKey, String propertyName, String workspaceId) {
try {
JSONObject json;
if (element instanceof Vertex) {
json = getBroadcastPropertyChangeJson((Vertex) element, propertyKey, propertyName, workspaceId);
} else if (element instanceof Edge) {
json = getBroadcastPropertyChangeJson((Edge) element, propertyKey, propertyName, workspaceId);
} else {
throw new LumifyException("Unexpected element type: " + element.getClass().getName());
}
broadcastJson(json);
} catch (Exception ex) {
throw new LumifyException("Could not broadcast property change", ex);
}
}
protected void broadcastEntityImage(Element element, String propertyKey, String propertyName) {
try {
JSONObject json = getBroadcastEntityImageJson((Vertex) element);
broadcastJson(json);
} catch (Exception ex) {
throw new LumifyException("Could not broadcast property change", ex);
}
}
protected abstract void broadcastJson(JSONObject json);
protected JSONObject getBroadcastEntityImageJson(Vertex graphVertex) {
// TODO: only broadcast to workspace users if sandboxStatus is PRIVATE
JSONObject json = new JSONObject();
json.put("type", "entityImageUpdated");
JSONObject dataJson = new JSONObject();
dataJson.put("graphVertexId", graphVertex.getId());
json.put("data", dataJson);
return json;
}
protected JSONObject getBroadcastPropertyChangeJson(Vertex graphVertex, String propertyKey, String propertyName, String workspaceId) {
// TODO: only broadcast to workspace users if sandboxStatus is PRIVATE
JSONObject json = new JSONObject();
json.put("type", "propertyChange");
JSONObject dataJson = new JSONObject();
dataJson.put("graphVertexId", graphVertex.getId());
dataJson.putOpt("workspaceId", workspaceId);
json.put("data", dataJson);
return json;
}
protected JSONObject getBroadcastPropertyChangeJson(Edge edge, String propertyKey, String propertyName, String workspaceId) {
// TODO: only broadcast to workspace users if sandboxStatus is PRIVATE
JSONObject json = new JSONObject();
json.put("type", "propertyChange");
JSONObject dataJson = new JSONObject();
dataJson.put("graphEdgeId", edge.getId());
dataJson.putOpt("workspaceId", workspaceId);
json.put("data", dataJson);
return json;
}
public abstract void pushOnQueue(String queueName, FlushFlag flushFlag, JSONObject json);
public void init(Map map) {
}
public abstract void flush();
public abstract void format();
public Graph getGraph() {
return graph;
}
public abstract void subscribeToBroadcastMessages(BroadcastConsumer broadcastConsumer);
public abstract LongRunningProcessMessage getNextLongRunningProcessMessage();
public abstract WorkerSpout createWorkerSpout();
public void shutdown() {
}
public void broadcastPublishVertexDelete(Vertex vertex) {
broadcastPublish(vertex, PublishType.DELETE);
}
public void broadcastPublishVertex(Vertex vertex) {
broadcastPublish(vertex, PublishType.TO_PUBLIC);
}
public void broadcastUndoVertexDelete(Vertex vertex) {
broadcastPublish(vertex, PublishType.UNDO_DELETE);
}
public void broadcastUndoVertex(Vertex vertex) {
broadcastPublish(vertex, PublishType.UNDO);
}
public void broadcastPublishPropertyDelete(Element element, String key, String name) {
broadcastPublish(element, key, name, PublishType.DELETE);
}
public void broadcastPublishProperty(Element element, String key, String name) {
broadcastPublish(element, key, name, PublishType.TO_PUBLIC);
}
public void broadcastUndoPropertyDelete(Element element, String key, String name) {
broadcastPublish(element, key, name, PublishType.UNDO_DELETE);
}
public void broadcastUndoProperty(Element element, String key, String name) {
broadcastPublish(element, key, name, PublishType.UNDO);
}
public void broadcastPublishEdgeDelete(Edge edge) {
broadcastPublish(edge, PublishType.DELETE);
}
public void broadcastPublishEdge(Edge edge) {
broadcastPublish(edge, PublishType.TO_PUBLIC);
}
public void broadcastUndoEdgeDelete(Edge edge) {
broadcastPublish(edge, PublishType.UNDO_DELETE);
}
public void broadcastUndoEdge(Edge edge) {
broadcastPublish(edge, PublishType.UNDO);
}
private void broadcastPublish(Element element, PublishType publishType) {
broadcastPublish(element, null, null, publishType);
}
private void broadcastPublish(Element element, String propertyKey, String propertyName, PublishType publishType) {
try {
JSONObject json;
if (element instanceof Vertex) {
json = getBroadcastPublishJson((Vertex) element, propertyKey, propertyName, publishType);
} else if (element instanceof Edge) {
json = getBroadcastPublishJson((Edge) element, propertyKey, propertyName, publishType);
} else {
throw new LumifyException("Unexpected element type: " + element.getClass().getName());
}
broadcastJson(json);
} catch (Exception ex) {
throw new LumifyException("Could not broadcast publish", ex);
}
}
protected JSONObject getBroadcastPublishJson(Vertex graphVertex, String propertyKey, String propertyName, PublishType publishType) {
JSONObject json = new JSONObject();
json.put("type", "publish");
JSONObject dataJson = new JSONObject();
dataJson.put("graphVertexId", graphVertex.getId());
dataJson.put("publishType", publishType.getJsonString());
if (propertyName == null) {
dataJson.put("objectType", "vertex");
} else {
dataJson.put("objectType", "property");
}
json.put("data", dataJson);
return json;
}
protected JSONObject getBroadcastPublishJson(Edge edge, String propertyKey, String propertyName, PublishType publishType) {
JSONObject json = new JSONObject();
json.put("type", "publish");
JSONObject dataJson = new JSONObject();
dataJson.put("graphEdgeId", edge.getId());
dataJson.put("publishType", publishType.getJsonString());
if (propertyName == null) {
dataJson.put("objectType", "edge");
} else {
dataJson.put("objectType", "property");
}
json.put("data", dataJson);
return json;
}
public static abstract class BroadcastConsumer {
public abstract void broadcastReceived(JSONObject json);
}
public static abstract class LongRunningProcessMessage {
private final JSONObject message;
public LongRunningProcessMessage(JSONObject message) {
this.message = message;
}
public JSONObject getMessage() {
return message;
}
public abstract void complete(Throwable ex);
public void complete() {
complete(null);
}
}
private enum PublishType {
TO_PUBLIC("toPublic"),
DELETE("delete"),
UNDO_DELETE("undoDelete"),
UNDO("undo");
private final String jsonString;
PublishType(String jsonString) {
this.jsonString = jsonString;
}
public String getJsonString() {
return jsonString;
}
}
}