update websocketmanager
[ccsdk/features.git] / sdnr / wt / websocketmanager / provider / src / main / java / org / onap / ccsdk / features / sdnr / wt / websocketmanager / WebSocketManagerSocket.java
1 /*
2 * ============LICENSE_START========================================================================
3  * ONAP : ccsdk feature sdnr wt
4  * =================================================================================================
5  * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved.
6  * =================================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
8  * in compliance with the License. You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software distributed under the License
13  * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
14  * or implied. See the License for the specific language governing permissions and limitations under
15  * the License.
16  * ============LICENSE_END==========================================================================
17  */
18 package org.onap.ccsdk.features.sdnr.wt.websocketmanager;
19
20 import com.fasterxml.jackson.core.JsonProcessingException;
21 import java.util.ArrayList;
22 import java.util.HashMap;
23 import java.util.List;
24 import java.util.Map;
25 import java.util.Map.Entry;
26 import java.util.Random;
27 import java.util.Set;
28 import java.util.regex.Matcher;
29 import java.util.regex.Pattern;
30 import org.eclipse.jetty.websocket.api.Session;
31 import org.eclipse.jetty.websocket.api.WebSocketAdapter;
32 import org.onap.ccsdk.features.sdnr.wt.websocketmanager.model.data.NotificationOutput;
33 import org.onap.ccsdk.features.sdnr.wt.websocketmanager.model.data.ReducedSchemaInfo;
34 import org.onap.ccsdk.features.sdnr.wt.websocketmanager.model.data.ScopeRegistration;
35 import org.onap.ccsdk.features.sdnr.wt.websocketmanager.model.data.ScopeRegistration.DataType;
36 import org.onap.ccsdk.features.sdnr.wt.websocketmanager.model.data.ScopeRegistrationResponse;
37 import org.onap.ccsdk.features.sdnr.wt.websocketmanager.utils.UserScopes;
38 import org.onap.ccsdk.features.sdnr.wt.yang.mapper.YangToolsMapper;
39 import org.slf4j.Logger;
40 import org.slf4j.LoggerFactory;
41
42 public class WebSocketManagerSocket extends WebSocketAdapter {
43
44     private static final Logger LOG = LoggerFactory.getLogger(WebSocketManagerSocket.class.getName());
45     public static final String MSG_KEY_DATA = "data";
46     public static final DataType MSG_KEY_SCOPES = DataType.scopes;
47     public static final String MSG_KEY_PARAM = "param";
48     public static final String MSG_KEY_VALUE = "value";
49     public static final String MSG_KEY_SCOPE = "scope";
50
51     public static final String KEY_NODEID = "nodeId";
52     public static final String KEY_EVENTTYPE = "eventType";
53     private static final String REGEX_SCOPEREGISTRATION = "\"data\"[\\s]*:[\\s]*\"scopes\"";
54     private static final Pattern PATTERN_SCOPEREGISTRATION =
55             Pattern.compile(REGEX_SCOPEREGISTRATION, Pattern.MULTILINE);
56     private static final Random RND = new Random();
57
58
59     /**
60      * list of all sessionids
61      */
62     private static final List<String> sessionIds = new ArrayList<>();
63     /**
64      * map of sessionid <=> UserScopes
65      */
66     private static final HashMap<String, UserScopes> userScopesList = new HashMap<>();
67     /**
68      * map of class.hashCode <=> class
69      */
70     private static final HashMap<String, WebSocketManagerSocket> clientList = new HashMap<>();
71
72     private static final YangToolsMapper mapper = new YangToolsMapper();
73     private final String myUniqueSessionId;
74
75     private Session session = null;
76
77     public interface EventInputCallback {
78         void onMessagePushed(final String message) throws Exception;
79     }
80
81     public WebSocketManagerSocket() {
82         this.myUniqueSessionId = _genSessionId();
83     }
84
85     @Override
86     protected void finalize() throws Throwable {
87         sessionIds.remove(this.myUniqueSessionId);
88     }
89
90     private static String _genSessionId() {
91         String sid = String.valueOf(RND.nextLong());
92         while (sessionIds.contains(sid)) {
93             sid = String.valueOf(RND.nextLong());
94         }
95         sessionIds.add(sid);
96         return sid;
97     }
98
99     @Override
100     public void onWebSocketText(String message) {
101         LOG.info("{} has sent {}", this.getRemoteAdr(), message);
102         if (!this.manageClientRequest(message)) {
103             this.manageClientRequest2(message);
104         }
105     }
106
107     @Override
108     public void onWebSocketBinary(byte[] payload, int offset, int len) {
109         LOG.debug("Binary not supported");
110     }
111
112     @Override
113     public void onWebSocketConnect(Session sess) {
114         this.session = sess;
115         clientList.put(String.valueOf(this.hashCode()), this);
116         LOG.debug("client connected from " + this.getRemoteAdr());
117     }
118
119     @Override
120     public void onWebSocketClose(int statusCode, String reason) {
121         clientList.remove(String.valueOf(this.hashCode()));
122         LOG.debug("client disconnected from " + this.getRemoteAdr());
123     }
124
125     @Override
126     public void onWebSocketError(Throwable cause) {
127         LOG.debug("error caused on " + this.getRemoteAdr() + " :" + cause.getMessage());
128         // super.onWebSocketError(cause);
129     }
130
131     private String getRemoteAdr() {
132         String adr = "unknown";
133         try {
134             adr = this.session.getRemoteAddress().toString();
135         } catch (Exception e) {
136             LOG.debug("error resolving adr: {}", e.getMessage());
137         }
138         return adr;
139     }
140
141     /**
142      *
143      * @param request is a json object {"data":"scopes","scopes":["scope1","scope2",...]}
144      * @return if handled
145      */
146     private boolean manageClientRequest(String request) {
147         boolean ret = false;
148         final Matcher matcher = PATTERN_SCOPEREGISTRATION.matcher(request);
149         if(!matcher.find()) {
150             return false;
151         }
152         try {
153             ScopeRegistration registration = mapper.readValue(request, ScopeRegistration.class);
154             if (registration!=null && registration.validate() && registration.isType(MSG_KEY_SCOPES)) {
155                 ret = true;
156                 String sessionId = this.getSessionId();
157                 UserScopes clientDto = new UserScopes();
158                 clientDto.setScopes(registration.getScopes());
159                 userScopesList.put(sessionId, clientDto);
160                 this.send(mapper.writeValueAsString(ScopeRegistrationResponse.success(registration.getScopes())));
161             }
162
163         } catch (JsonProcessingException e) {
164             LOG.warn("problem set scope: " + e.getMessage());
165             try {
166                 this.send(mapper.writeValueAsString(ScopeRegistrationResponse.error(e.getMessage())));
167             } catch (JsonProcessingException e1) {
168                 LOG.warn("problem sending error response via ws: " + e1);
169             }
170         }
171         return ret;
172     }
173
174     /*
175      * broadcast message to all your clients
176      */
177     private void manageClientRequest2(String request) {
178         try {
179             NotificationOutput notification = mapper.readValue(request, NotificationOutput.class);
180             if (notification.getNodeId() != null && notification.getType() != null) {
181                 this.sendToAll(notification.getNodeId(), notification.getType(), request);
182             }
183         } catch (Exception e) {
184             LOG.warn("handle ws request failed:" + e.getMessage());
185         }
186     }
187
188     public void send(String msg) {
189         try {
190             LOG.trace("sending {}", msg);
191             this.session.getRemote().sendString(msg);
192         } catch (Exception e) {
193             LOG.warn("problem sending message: " + e.getMessage());
194         }
195     }
196
197     public String getSessionId() {
198         return this.myUniqueSessionId;
199     }
200
201     private void sendToAll(NotificationOutput output) {
202         try {
203             this.sendToAll(output.getNodeId(), output.getType(), mapper.writeValueAsString(output));
204         } catch (JsonProcessingException e) {
205             LOG.warn("problem serializing noitifcation: ", e);
206         }
207     }
208
209     private void sendToAll(String nodeId, ReducedSchemaInfo reducedSchemaInfo, String notification) {
210         if (clientList.size() > 0) {
211             for (Map.Entry<String, WebSocketManagerSocket> entry : clientList.entrySet()) {
212                 WebSocketManagerSocket socket = entry.getValue();
213                 if (socket != null) {
214                     try {
215                         UserScopes clientScopes = userScopesList.get(socket.getSessionId());
216                         if (clientScopes != null) {
217                             if (clientScopes.hasScope(nodeId, reducedSchemaInfo)) {
218                                 socket.send(notification);
219                             } else {
220                                 LOG.debug("client has not scope {}", reducedSchemaInfo);
221                             }
222                         } else {
223                             LOG.debug("no scopes for notifications registered");
224                         }
225                     } catch (Exception ioe) {
226                         LOG.warn(ioe.getMessage());
227                     }
228                 } else {
229                     LOG.debug("cannot broadcast. socket is null");
230                 }
231             }
232         }
233     }
234
235     public static void broadCast(NotificationOutput output) {
236         if (clientList.size() > 0) {
237             Set<Entry<String, WebSocketManagerSocket>> e = clientList.entrySet();
238             WebSocketManagerSocket s = e.iterator().next().getValue();
239             if (s != null) {
240                 s.sendToAll(output);
241             }
242         }
243     }
244
245 }