use of org.structr.websocket.message.WebSocketMessage in project structr by structr.
the class StructrWebSocket method onWebSocketText.
@Override
public void onWebSocketText(final String data) {
if (!Services.getInstance().isInitialized()) {
// send 401 Authentication Required
send(MessageBuilder.status().code(503).message("System is not initialized yet").build(), true);
}
final Services servicesInstance = Services.getInstance();
// wait for service layer to be initialized
while (!servicesInstance.isInitialized()) {
try {
Thread.sleep(1000);
} catch (InterruptedException iex) {
}
}
if (data == null) {
logger.warn("Empty text message received.");
return;
}
logger.debug("############################################################ RECEIVED \n{}", data.substring(0, Math.min(data.length(), 1000)));
// parse web socket data from JSON
final WebSocketMessage webSocketData = gson.fromJson(data, WebSocketMessage.class);
final App app = StructrApp.getInstance(securityContext);
final String command = webSocketData.getCommand();
final Class type = commandSet.get(command);
final String sessionIdFromMessage = webSocketData.getSessionId();
if (type != null) {
try (final Tx tx = app.tx()) {
if (sessionIdFromMessage != null) {
// try to authenticated this connection by sessionId
authenticate(SessionHelper.getShortSessionId(sessionIdFromMessage), command.equals("PING"));
}
// we only permit LOGIN commands if authentication based on sessionId was not successful
if (!isAuthenticated() && !type.equals(LoginCommand.class)) {
// send 401 Authentication Required
send(MessageBuilder.status().code(401).message("").build(), true);
tx.success();
return;
}
tx.success();
} catch (DatabaseServiceNotAvailableException dbsnae) {
logger.warn(dbsnae.getMessage());
} catch (FrameworkException t) {
logger.warn("Unable to parse message.", t);
}
// process message
try {
AbstractCommand abstractCommand = (AbstractCommand) type.newInstance();
abstractCommand.setWebSocket(this);
abstractCommand.setSession(session);
abstractCommand.setCallback(webSocketData.getCallback());
if (!(abstractCommand instanceof PingCommand)) {
if (securityContext != null) {
final HttpSession session = SessionHelper.getSessionBySessionId(securityContext.getSessionId());
if (session != null) {
session.setMaxInactiveInterval(Services.getGlobalSessionTimeout());
try {
// Workaround to update lastAccessedTime() in Jetty's session via reflection
final Method accessMethod = ((org.eclipse.jetty.server.session.Session) session).getClass().getDeclaredMethod("access", long.class);
accessMethod.setAccessible(true);
accessMethod.invoke((org.eclipse.jetty.server.session.Session) session, System.currentTimeMillis());
} catch (Exception ex) {
logger.error("Access to method Session.access() via reflection failed: ", ex);
}
}
}
}
// transactions in case of bulk processing commands etc.
if (abstractCommand.requiresEnclosingTransaction()) {
try (final Tx tx = app.tx()) {
// store authenticated-Flag in webSocketData
// so the command can access it
webSocketData.setSessionValid(isAuthenticated());
abstractCommand.processMessage(webSocketData);
// commit transaction
tx.success();
}
} else {
try (final Tx tx = app.tx()) {
// store authenticated-Flag in webSocketData
// so the command can access it
webSocketData.setSessionValid(isAuthenticated());
// commit transaction
tx.success();
}
// process message without transaction context!
abstractCommand.processMessage(webSocketData);
}
} catch (FrameworkException | InstantiationException | IllegalAccessException t) {
try (final Tx tx = app.tx()) {
// send 400 Bad Request
if (t instanceof FrameworkException) {
final FrameworkException fex = (FrameworkException) t;
send(MessageBuilder.status().code(fex.getStatus()).message(fex.toString()).jsonErrorObject(fex.toJSON()).callback(webSocketData.getCallback()).build(), true);
} else {
send(MessageBuilder.status().code(400).message(t.toString()).build(), true);
}
// commit transaction
tx.success();
} catch (FrameworkException fex) {
logger.warn("Unable to send websocket result: {}", fex.getMessage());
}
return;
}
} else {
logger.warn("Unknown command {}", command);
// send 400 Bad Request
send(MessageBuilder.status().code(400).message("Unknown command").build(), true);
return;
}
}
use of org.structr.websocket.message.WebSocketMessage in project structr by structr.
the class WebsocketController method getMessageForEvent.
// ----- private methods -----
private WebSocketMessage getMessageForEvent(final SecurityContext securityContext, final ModificationEvent modificationEvent) throws FrameworkException {
final String callbackId = modificationEvent.getCallbackId();
if (modificationEvent.isNode()) {
final NodeInterface node = (NodeInterface) modificationEvent.getGraphObject();
if (modificationEvent.isDeleted()) {
final WebSocketMessage message = createMessage("DELETE", callbackId);
message.setId(modificationEvent.getRemovedProperties().get(GraphObject.id));
message.setCode(200);
return message;
}
if (modificationEvent.isCreated()) {
final WebSocketMessage message = createMessage("CREATE", callbackId);
message.setGraphObject(node);
message.setResult(Arrays.asList(new GraphObject[] { node }));
message.setCode(201);
return message;
}
if (modificationEvent.isModified()) {
final WebSocketMessage message = createMessage("UPDATE", callbackId);
// at login the securityContext is still null
if (securityContext != null) {
// only include changed properties (+ id and type)
LinkedHashSet<String> propertySet = new LinkedHashSet();
propertySet.add("id");
propertySet.add("type");
for (Iterator<PropertyKey> it = modificationEvent.getModifiedProperties().keySet().iterator(); it.hasNext(); ) {
final String jsonName = ((PropertyKey) it.next()).jsonName();
if (!propertySet.contains(jsonName)) {
propertySet.add(jsonName);
}
}
for (Iterator<PropertyKey> it = modificationEvent.getRemovedProperties().keySet().iterator(); it.hasNext(); ) {
final String jsonName = ((PropertyKey) it.next()).jsonName();
if (!propertySet.contains(jsonName)) {
propertySet.add(jsonName);
}
}
if (propertySet.size() > 2) {
securityContext.setCustomView(propertySet);
}
}
message.setGraphObject(node);
message.setResult(Arrays.asList(new GraphObject[] { node }));
message.setId(node.getUuid());
message.getModifiedProperties().addAll(modificationEvent.getModifiedProperties().keySet());
message.getRemovedProperties().addAll(modificationEvent.getRemovedProperties().keySet());
message.setNodeData(modificationEvent.getData(securityContext));
message.setCode(200);
if (securityContext != null) {
// Clear custom view here. This is necessary because the security context is reused for all websocket frames.
securityContext.clearCustomView();
}
return message;
}
} else {
// handle relationship
final RelationshipInterface relationship = (RelationshipInterface) modificationEvent.getGraphObject();
final RelationshipType relType = modificationEvent.getRelationshipType();
// special treatment of CONTAINS relationships
if ("CONTAINS".equals(relType.name())) {
if (modificationEvent.isDeleted()) {
final WebSocketMessage message = createMessage("REMOVE_CHILD", callbackId);
message.setNodeData("parentId", relationship.getSourceNodeId());
message.setId(relationship.getTargetNodeId());
message.setCode(200);
return message;
}
if (modificationEvent.isCreated()) {
final WebSocketMessage message = new WebSocketMessage();
final NodeInterface startNode = relationship.getSourceNode();
final NodeInterface endNode = relationship.getTargetNode();
// don't send a notification
if (startNode == null || endNode == null) {
return null;
}
message.setResult(Arrays.asList(new GraphObject[] { endNode }));
message.setId(endNode.getUuid());
message.setNodeData("parentId", startNode.getUuid());
message.setCode(200);
message.setCommand("APPEND_CHILD");
if (endNode instanceof DOMNode) {
org.w3c.dom.Node refNode = ((DOMNode) endNode).getNextSibling();
if (refNode != null) {
message.setCommand("INSERT_BEFORE");
message.setNodeData("refId", ((AbstractNode) refNode).getUuid());
}
} else if (endNode instanceof User) {
message.setCommand("APPEND_USER");
message.setNodeData("refId", startNode.getUuid());
} else if (endNode instanceof AbstractFile) {
message.setCommand("APPEND_FILE");
message.setNodeData("refId", startNode.getUuid());
}
return message;
}
}
if (modificationEvent.isDeleted()) {
final WebSocketMessage message = createMessage("DELETE", callbackId);
message.setId(modificationEvent.getRemovedProperties().get(GraphObject.id));
message.setCode(200);
return message;
}
if (modificationEvent.isModified()) {
final WebSocketMessage message = createMessage("UPDATE", callbackId);
message.getModifiedProperties().addAll(modificationEvent.getModifiedProperties().keySet());
message.getRemovedProperties().addAll(modificationEvent.getRemovedProperties().keySet());
message.setNodeData(modificationEvent.getData(securityContext));
message.setGraphObject(relationship);
message.setId(relationship.getUuid());
message.setCode(200);
final PropertyMap relProperties = relationship.getProperties();
// final NodeInterface startNode = relationship.getSourceNode();
// final NodeInterface endNode = relationship.getTargetNode();
// relProperties.put(new StringProperty("startNodeId"), startNode.getUuid());
// relProperties.put(new StringProperty("endNodeId"), endNode.getUuid());
final Map<String, Object> properties = PropertyMap.javaTypeToInputType(securityContext, relationship.getClass(), relProperties);
message.setRelData(properties);
return message;
}
}
return null;
}
use of org.structr.websocket.message.WebSocketMessage in project structr by structr.
the class WebsocketController method broadcast.
private void broadcast(final WebSocketMessage webSocketData, final String exemptedSessionId) {
// session must be valid to be received by the client
webSocketData.setSessionValid(true);
final String pagePath = (String) webSocketData.getNodeData().get("pagePath");
final String encodedPath = URIUtil.encodePath(pagePath);
final List<StructrWebSocket> clientsToRemove = new LinkedList<>();
final List<? extends GraphObject> result = webSocketData.getResult();
final String command = webSocketData.getCommand();
final GraphObject obj = webSocketData.getGraphObject();
String message;
// create message
for (StructrWebSocket socket : clients) {
String clientPagePath = socket.getPagePath();
if (clientPagePath != null && !clientPagePath.equals(encodedPath)) {
continue;
}
Session session = socket.getSession();
if (session != null && socket.isAuthenticated()) {
final SecurityContext securityContext = socket.getSecurityContext();
if (exemptedSessionId != null && exemptedSessionId.equals(securityContext.getSessionId())) {
// session id is supposed to be exempted from this broadcast message
continue;
}
// THEN skip sending a message
if (obj instanceof AbstractNode) {
final AbstractNode node = (AbstractNode) obj;
if (node.isHidden() || !securityContext.isVisible(node)) {
continue;
}
} else {
if (!socket.isPrivilegedUser(socket.getCurrentUser())) {
continue;
}
}
if (result != null && !result.isEmpty() && BroadcastCommands.contains(command)) {
final WebSocketMessage clientData = webSocketData.copy();
clientData.setResult(filter(securityContext, result));
message = gson.toJson(clientData, WebSocketMessage.class);
} else {
message = gson.toJson(webSocketData, WebSocketMessage.class);
}
try {
session.getRemote().sendString(message);
} catch (Throwable t) {
if (t instanceof WebSocketException) {
WebSocketException wse = (WebSocketException) t;
if ("RemoteEndpoint unavailable, current state [CLOSED], expecting [OPEN or CONNECTED]".equals(wse.getMessage())) {
clientsToRemove.add(socket);
}
}
logger.debug("Error sending message to client.", t);
}
}
}
for (StructrWebSocket s : clientsToRemove) {
unregisterClient(s);
logger.warn("Client removed from broadcast list: {}", s);
}
}
use of org.structr.websocket.message.WebSocketMessage in project structr by structr.
the class WebsocketController method createMessage.
private WebSocketMessage createMessage(final String command, final String callbackId) {
final WebSocketMessage newMessage = new WebSocketMessage();
newMessage.setCommand(command);
if (callbackId != null) {
newMessage.setCallback(callbackId);
}
return newMessage;
}
use of org.structr.websocket.message.WebSocketMessage in project structr by structr.
the class WebSocketDataGSONAdapter method deserialize.
@Override
public WebSocketMessage deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
WebSocketMessage webSocketData = new WebSocketMessage();
if (json instanceof JsonObject) {
JsonObject root = json.getAsJsonObject();
JsonObject nodeData = root.getAsJsonObject("data");
JsonObject relData = root.getAsJsonObject("relData");
if (root.has("command")) {
webSocketData.setCommand(root.getAsJsonPrimitive("command").getAsString());
}
if (root.has("id")) {
webSocketData.setId(root.getAsJsonPrimitive("id").getAsString());
}
if (root.has("pageId")) {
webSocketData.setPageId(root.getAsJsonPrimitive("pageId").getAsString());
}
if (root.has("sessionId")) {
JsonPrimitive sessionId = root.getAsJsonPrimitive("sessionId");
if (sessionId != null) {
webSocketData.setSessionId(sessionId.getAsString());
}
}
if (root.has("callback")) {
webSocketData.setCallback(root.getAsJsonPrimitive("callback").getAsString());
}
if (root.has("button")) {
webSocketData.setButton(root.getAsJsonPrimitive("button").getAsString());
}
if (root.has("parent")) {
webSocketData.setParent(root.getAsJsonPrimitive("parent").getAsString());
}
if (root.has("view")) {
webSocketData.setView(root.getAsJsonPrimitive("view").getAsString());
}
if (root.has("sort")) {
webSocketData.setSortKey(root.getAsJsonPrimitive("sort").getAsString());
}
if (root.has("order")) {
webSocketData.setSortOrder(root.getAsJsonPrimitive("order").getAsString());
}
if (root.has("pageSize")) {
webSocketData.setPageSize(root.getAsJsonPrimitive("pageSize").getAsInt());
}
if (root.has("page")) {
webSocketData.setPage(root.getAsJsonPrimitive("page").getAsInt());
}
if (nodeData != null) {
JsonInputGSONAdapter adapter = new JsonInputGSONAdapter();
for (Entry<String, JsonElement> entry : nodeData.entrySet()) {
final JsonElement obj = entry.getValue();
Object value = null;
if (obj instanceof JsonPrimitive) {
value = adapter.fromPrimitive(obj.getAsJsonPrimitive());
} else if (obj instanceof JsonObject) {
value = adapter.deserialize(obj, typeOfT, context);
} else if (obj instanceof JsonArray) {
final JsonArray array = obj.getAsJsonArray();
final List list = new LinkedList();
for (JsonElement element : array) {
if (element.isJsonPrimitive()) {
list.add(fromPrimitive((element.getAsJsonPrimitive())));
} else if (element.isJsonObject()) {
// create map of values
list.add(JsonInputGSONAdapter.deserialize(element, context));
}
}
value = list;
} else if (obj instanceof JsonNull) {
value = null;
} else if (obj != null) {
value = obj.getAsString();
}
webSocketData.setNodeData(entry.getKey(), value);
}
}
if (relData != null) {
for (Entry<String, JsonElement> entry : relData.entrySet()) {
JsonElement obj = entry.getValue();
if (obj instanceof JsonNull || obj.isJsonNull()) {
webSocketData.setRelData(entry.getKey(), null);
} else {
try {
webSocketData.setRelData(entry.getKey(), obj.getAsString());
} catch (Throwable t) {
webSocketData.setRelData(entry.getKey(), null);
}
}
}
}
}
return webSocketData;
}
Aggregations