diff --git a/vipra-backend/src/main/java/de/vipra/rest/resource/TopicResource.java b/vipra-backend/src/main/java/de/vipra/rest/resource/TopicResource.java index 6a0d7bde5fdc71e57f17b988124d9865e4e4b0f7..619d28c86c904fe6d92f5d6a4fa9f2d90b016cd9 100644 --- a/vipra-backend/src/main/java/de/vipra/rest/resource/TopicResource.java +++ b/vipra-backend/src/main/java/de/vipra/rest/resource/TopicResource.java @@ -34,6 +34,7 @@ import de.vipra.util.model.Topic; import de.vipra.util.model.TopicFull; import de.vipra.util.service.MongoService; import de.vipra.util.service.Service.QueryBuilder; +import de.vipra.ws.WebSocket; @Path("topics") public class TopicResource { @@ -137,6 +138,7 @@ public class TopicResource { try { dbTopics.replaceSingle(topic); + WebSocket.sendToState("topics.show", "{\"msg\":\"topic updated\"}"); return res.ok(topic); } catch (DatabaseException e) { e.printStackTrace(); diff --git a/vipra-backend/src/main/java/de/vipra/ws/WebSocket.java b/vipra-backend/src/main/java/de/vipra/ws/WebSocket.java index 5db32f50194a4d371ca7c8861ed9a4e50967f3db..5e7fa899b8441c656cef0393d7a3697b1e09e3d1 100644 --- a/vipra-backend/src/main/java/de/vipra/ws/WebSocket.java +++ b/vipra-backend/src/main/java/de/vipra/ws/WebSocket.java @@ -1,6 +1,9 @@ package de.vipra.ws; import java.io.IOException; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; import javax.websocket.OnClose; import javax.websocket.OnError; @@ -12,33 +15,69 @@ import javax.websocket.server.ServerEndpoint; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +import de.vipra.util.MultiMap; +import de.vipra.ws.msg.InitMessage; +import de.vipra.ws.msg.WebSocketMessage; + @ServerEndpoint("/ws") public class WebSocket { public static final Logger log = LogManager.getLogger(WebSocket.class); + public static final ObjectMapper mapper = new ObjectMapper(); + public static final Set<Session> sessions = new HashSet<>(); + public static final MultiMap<String, Session> states = new MultiMap<>(); + @OnOpen public void open(Session session) { - log.info("open"); + log.debug("connect"); + sessions.add(session); } @OnClose public void close(Session session) { - log.info("close"); + log.debug("disconnect"); + sessions.remove(session); } @OnError - public void onError(Throwable error) { - log.info("error"); - } + public void onError(Throwable error) {} @OnMessage - public void handleMessage(String message, Session session) { - log.info(message); + public void handleMessage(String input, Session session) + throws JsonParseException, JsonMappingException, IOException { + log.trace("message received"); try { - session.getBasicRemote().sendText(message); + WebSocketMessage msg = mapper.readValue(input, WebSocketMessage.class); + switch (msg.getType()) { + case 1: + handleInitMessage(mapper.readValue(msg.getData(), InitMessage.class), session); + break; + } } catch (IOException e) { - e.printStackTrace(); + log.error(e); + } + } + + public void handleInitMessage(InitMessage message, Session session) { + log.debug("init message received. state = " + message.getState()); + states.put(message.getState(), session); + } + + public static void sendToState(String state, String message) { + Collection<Session> sessions = states.get(state); + if (sessions != null) { + for (Session session : sessions) { + try { + session.getBasicRemote().sendText(message); + } catch (IOException e) { + log.error(e); + } + } } } diff --git a/vipra-backend/src/main/java/de/vipra/ws/msg/InitMessage.java b/vipra-backend/src/main/java/de/vipra/ws/msg/InitMessage.java new file mode 100644 index 0000000000000000000000000000000000000000..ab5054001839622fb68e2d57b16dc1f5153944b7 --- /dev/null +++ b/vipra-backend/src/main/java/de/vipra/ws/msg/InitMessage.java @@ -0,0 +1,15 @@ +package de.vipra.ws.msg; + +public class InitMessage extends WebSocketMessage { + + private String state; + + public String getState() { + return state; + } + + public void setState(String state) { + this.state = state; + } + +} diff --git a/vipra-backend/src/main/java/de/vipra/ws/msg/WebSocketMessage.java b/vipra-backend/src/main/java/de/vipra/ws/msg/WebSocketMessage.java new file mode 100644 index 0000000000000000000000000000000000000000..1c2b586ae3270551a4b0c66489e6ee2e75cde61b --- /dev/null +++ b/vipra-backend/src/main/java/de/vipra/ws/msg/WebSocketMessage.java @@ -0,0 +1,25 @@ +package de.vipra.ws.msg; + +public class WebSocketMessage { + + private int type; + + private String data; + + public int getType() { + return type; + } + + public void setType(int type) { + this.type = type; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + +} diff --git a/vipra-ui/app/js/config.js b/vipra-ui/app/js/config.js index 50df7e92d2b6b2f912a8aa287bb73a98c83688bd..e57dd2d136d48076a8f2371e62539307ed3b4995 100644 --- a/vipra-ui/app/js/config.js +++ b/vipra-ui/app/js/config.js @@ -4,7 +4,7 @@ Vipra.config = { restUrl: '//' + location.hostname + ':8080/vipra/rest', - websocketUrl: '//' + location.hostname + ':8080/vipra/ws' + websocketUrl: 'ws://' + location.hostname + ':8080/vipra/ws' }; })(); \ No newline at end of file diff --git a/vipra-ui/app/js/controllers.js b/vipra-ui/app/js/controllers.js index cbe753d031d6c67f79a400d9cf3382e3c82f5369..326c167905ce52582fad717935b19d4a27495be6 100644 --- a/vipra-ui/app/js/controllers.js +++ b/vipra-ui/app/js/controllers.js @@ -371,8 +371,8 @@ /** * Topic Show route */ - app.controller('TopicsShowController', ['$scope', '$stateParams', '$timeout', 'TopicFactory', - function($scope, $stateParams, $timeout, TopicFactory) { + app.controller('TopicsShowController', ['$scope', '$stateParams', '$timeout', 'TopicFactory', 'WebSocketService', + function($scope, $stateParams, $timeout, TopicFactory, WebSocketService) { $scope.wordSort = $scope.wordSort || 'likeliness'; $scope.wordSortRev = typeof $scope.wordSortRev === 'undefined' ? true : $scope.wordSortRev; @@ -412,7 +412,6 @@ $event.preventDefault(); } }; - }]); /** diff --git a/vipra-ui/app/js/factories.js b/vipra-ui/app/js/factories.js index 54b50fe55f4b5681cc1e7e35444216ab9acd54f4..a88b88b98c341d8e374b28b13dd62da437883469 100644 --- a/vipra-ui/app/js/factories.js +++ b/vipra-ui/app/js/factories.js @@ -99,4 +99,25 @@ }; }]); + app.service('WebSocketService', ['$websocket', '$state', function($websocket, $state) { + var socket = $websocket(Vipra.config.websocketUrl), + callback = null; + + socket.onMessage(function(message) { + var data = JSON.parse(message.data); + if(callback) + callback(data); + }); + + this.send = function(type, data) { + socket.send(JSON.stringify({ type: type, data: JSON.stringify(data) })); + }; + + this.receive = function(fn) { + callback = fn; + }; + + this.send(1, {state: $state.current.name}); + }]); + })(); \ No newline at end of file diff --git a/vipra-util/src/main/java/de/vipra/util/MultiMap.java b/vipra-util/src/main/java/de/vipra/util/MultiMap.java new file mode 100644 index 0000000000000000000000000000000000000000..6f0fe47f3f1784eec7c695bdb0ebf47b38f83999 --- /dev/null +++ b/vipra-util/src/main/java/de/vipra/util/MultiMap.java @@ -0,0 +1,104 @@ +package de.vipra.util; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +public class MultiMap<T, U> implements Map<T, Collection<U>> { + + private final Map<T, Collection<U>> map; + private final boolean unique; + + public MultiMap() { + this(false); + } + + public MultiMap(boolean unique) { + this.map = new HashMap<>(); + this.unique = unique; + } + + @Override + public int size() { + return map.size(); + } + + @Override + public boolean isEmpty() { + return map.isEmpty(); + } + + @Override + public boolean containsKey(Object key) { + return map.containsKey(key); + } + + @Override + public boolean containsValue(Object value) { + return map.containsValue(value); + } + + @Override + public Collection<U> get(Object key) { + return map.get(key); + } + + public Iterator<U> each(Object key) { + Collection<U> c = map.get(key); + if (c == null) + return null; + return c.iterator(); + } + + @Override + public Collection<U> put(T key, Collection<U> value) { + return map.put(key, value); + } + + public void put(T key, U value) { + Collection<U> c = map.get(key); + if (c == null) { + if (unique) + c = new HashSet<>(); + else + c = new ArrayList<>(); + } + c.add(value); + map.put(key, c); + } + + @Override + public Collection<U> remove(Object key) { + return map.remove(key); + } + + @Override + public void putAll(Map<? extends T, ? extends Collection<U>> m) { + map.putAll(m); + } + + @Override + public void clear() { + map.clear(); + } + + @Override + public Set<T> keySet() { + return map.keySet(); + } + + @Override + public Collection<Collection<U>> values() { + return map.values(); + } + + @Override + public Set<java.util.Map.Entry<T, Collection<U>>> entrySet() { + return map.entrySet(); + } + +}