/* * To change this template, choose Tools | Templates * and open the template in the editor. */ package com.eas.server.websocket; import com.eas.client.ClientConstants; import com.eas.client.DatabasesClient; import com.eas.client.login.AnonymousPlatypusPrincipal; import com.eas.client.login.PlatypusPrincipal; import com.eas.script.Scripts; import com.eas.server.PlatypusServerCore; import com.eas.server.SessionManager; import com.eas.server.httpservlet.PlatypusHttpServlet; import com.eas.util.IdGenerator; import java.util.Map; import java.util.function.Consumer; import java.util.logging.Level; import java.util.logging.Logger; import javax.script.ScriptException; import javax.websocket.CloseReason; import javax.websocket.OnClose; import javax.websocket.OnError; import javax.websocket.OnMessage; import javax.websocket.OnOpen; import javax.websocket.Session; import javax.websocket.server.HandshakeRequest; import javax.websocket.server.PathParam; import javax.websocket.server.ServerEndpoint; import jdk.nashorn.api.scripting.JSObject; /** * * @author mg */ @ServerEndpoint(value = "/{module-name}", configurator = JsServerModuleEndPointConfigurator.class) public class JsServerModuleEndPoint { // private static final String WS_ON_OPEN = "onopen"; private static final String WS_ON_MESSAGE = "onmessage"; private static final String WS_ON_CLOSE = "onclose"; private static final String WS_ON_ERROR = "onerror"; protected volatile String wasPlatypusSessionId; protected volatile String moduleName; public JsServerModuleEndPoint() { super(); } protected void in(PlatypusServerCore platypusCore, Session websocketSession, Consumer<com.eas.server.Session> aHandler) throws Exception { HandshakeRequest handshake = (HandshakeRequest) websocketSession.getUserProperties().get(JsServerModuleEndPointConfigurator.HANDSHAKE_REQUEST); String userName = websocketSession.getUserPrincipal() != null ? websocketSession.getUserPrincipal().getName() : null; Consumer<com.eas.server.Session> withPlatypusSession = (com.eas.server.Session aSession) -> { // Websocket executor thread or sessions accounting thread Scripts.LocalContext context = new Scripts.LocalContext(platypusPrincipal(handshake, websocketSession), aSession); aSession.getSpace().process(context, () -> { // temporarily session thread try { aHandler.accept(aSession); } catch (Exception ex) { Logger.getLogger(PlatypusHttpServlet.class.getName()).log(Level.SEVERE, null, ex); } }); }; com.eas.server.Session lookedup0 = platypusSessionByWebSocketSession(websocketSession); if (lookedup0 == null) {// Zombie check platypusCore.getQueueSpace().process(() -> { // sessions accounting thread com.eas.server.Session lookedup1 = platypusSessionByWebSocketSession(websocketSession); if (lookedup1 == null) {// Zombie check try { Consumer<String> withDataContext = (String dataContext) -> { // still sessions accounting thread String platypusSessionId = (String) websocketSession.getUserProperties().get(PlatypusHttpServlet.PLATYPUS_SESSION_ID_ATTR_NAME); // platypusSessionId may be replicated from another instance in cluster com.eas.server.Session lookedup2 = platypusSessionId != null ? SessionManager.Singleton.instance.get(platypusSessionId) : null; if (lookedup2 == null) {// Non zombie check try { // preserve replicated session id com.eas.server.Session created = SessionManager.Singleton.instance.create(platypusSessionId == null ? IdGenerator.genId() + "" : platypusSessionId); if (dataContext == null) { websocketSession.getUserProperties().remove(PlatypusHttpServlet.PLATYPUS_USER_CONTEXT_ATTR_NAME); } else { websocketSession.getUserProperties().put(PlatypusHttpServlet.PLATYPUS_USER_CONTEXT_ATTR_NAME, dataContext); } // publishing a session wasPlatypusSessionId = created.getId(); websocketSession.getUserProperties().put(PlatypusHttpServlet.PLATYPUS_SESSION_ID_ATTR_NAME, created.getId()); // a session has been published Logger.getLogger(JsServerModuleEndPoint.class.getName()).log(Level.INFO, "WebSocket platypus session opened. Session id: {0}", created.getId()); withPlatypusSession.accept(created); } catch (ScriptException ex) { Logger.getLogger(JsServerModuleEndPoint.class.getName()).log(Level.SEVERE, null, ex); } } else { withPlatypusSession.accept(lookedup2); } }; if (websocketSession.getUserPrincipal() != null) {// Additional properties can be obtained only for authorized users DatabasesClient.getUserProperties(platypusCore.getDatabasesClient(), userName, platypusCore.getQueueSpace(), (Map<String, String> aUserProps) -> { // still sessions accounting thread String dataContext = aUserProps.get(ClientConstants.F_USR_CONTEXT); withDataContext.accept(dataContext); }, (Exception ex) -> { // still sessions accounting thread Logger.getLogger(PlatypusHttpServlet.class.getName()).log(Level.FINE, "Unable to obtain properties of user {0} due to an error: {1}", new Object[]{userName, ex.toString()}); withDataContext.accept(null); }); } else { withDataContext.accept(null); } } catch (Exception ex) { Logger.getLogger(PlatypusHttpServlet.class.getName()).log(Level.SEVERE, null, ex); } } else { withPlatypusSession.accept(lookedup1); } }); } else { withPlatypusSession.accept(lookedup0); } } public PlatypusPrincipal platypusPrincipal(HandshakeRequest handshake, Session websocketSession) { PlatypusPrincipal principal; if (handshake.getUserPrincipal() != null) { principal = new WebSocketPlatypusPrincipal(handshake.getUserPrincipal().getName(), (String) websocketSession.getUserProperties().get(PlatypusHttpServlet.PLATYPUS_USER_CONTEXT_ATTR_NAME), handshake); } else { principal = new AnonymousPlatypusPrincipal(websocketSession.getId()); } return principal; } public com.eas.server.Session platypusSessionByWebSocketSession(Session websocketSession) { String platypusSessionId = (String) websocketSession.getUserProperties().get(PlatypusHttpServlet.PLATYPUS_SESSION_ID_ATTR_NAME); return platypusSessionId != null ? SessionManager.Singleton.instance.get(platypusSessionId) : null; } @OnOpen public void sessionOpened(Session websocketSession, @PathParam("module-name") String aModuleName) throws Exception { moduleName = aModuleName; PlatypusServerCore platypusCore = lookupPlaypusServerCore(); in(platypusCore, websocketSession, (com.eas.server.Session aSession) -> { try { Logger.getLogger(JsServerModuleEndPoint.class.getName()).log(Level.FINE, "WebSocket container OnOpen {0}.", aSession.getId()); platypusCore.executeMethod(moduleName, WS_ON_OPEN, new Object[]{new WebSocketServerSession(websocketSession)}, true, (Object aResult) -> { Logger.getLogger(JsServerModuleEndPoint.class.getName()).log(Level.FINE, "{0} method of {1} module called successfully.", new Object[]{WS_ON_OPEN, aModuleName}); }, (Exception ex) -> { Logger.getLogger(JsServerModuleEndPoint.class.getName()).log(Level.SEVERE, null, ex); }); } catch (Exception ex) { Logger.getLogger(JsServerModuleEndPoint.class.getName()).log(Level.SEVERE, null, ex); } }); } @OnMessage public void messageRecieved(Session websocketSession, String aData) throws Exception { PlatypusServerCore platypusCore = lookupPlaypusServerCore(); in(platypusCore, websocketSession, (com.eas.server.Session aSession) -> { Logger.getLogger(JsServerModuleEndPoint.class.getName()).log(Level.FINE, "WebSocket container OnMessage {0}.", aSession.getId()); JSObject messageEvent = Scripts.getSpace().makeObj(); messageEvent.setMember("data", aData); messageEvent.setMember("id", websocketSession.getId()); platypusCore.executeMethod(moduleName, WS_ON_MESSAGE, new Object[]{messageEvent}, true, (Object aResult) -> { Logger.getLogger(JsServerModuleEndPoint.class.getName()).log(Level.FINE, "{0} method of {1} module called successfully.", new Object[]{WS_ON_MESSAGE, moduleName}); }, (Exception ex) -> { Logger.getLogger(JsServerModuleEndPoint.class.getName()).log(Level.SEVERE, null, ex); }); }); } @OnError public void errorInSession(Session websocketSession, Throwable aError) throws Exception { PlatypusServerCore platypusCore = lookupPlaypusServerCore(); in(platypusCore, websocketSession, (com.eas.server.Session aSession) -> { Logger.getLogger(JsServerModuleEndPoint.class.getName()).log(Level.FINE, "WebSocket container OnError {0}.", aSession.getId()); JSObject errorEvent = Scripts.getSpace().makeObj(); errorEvent.setMember("message", aError.getMessage()); errorEvent.setMember("id", websocketSession.getId()); Logger.getLogger(JsServerModuleEndPoint.class.getName()).log(Level.SEVERE, null, aError); platypusCore.executeMethod(moduleName, WS_ON_ERROR, new Object[]{errorEvent}, true, (Object aResult) -> { Logger.getLogger(JsServerModuleEndPoint.class.getName()).log(Level.FINE, "{0} method of {1} module called successfully.", new Object[]{WS_ON_ERROR, moduleName}); }, (Exception ex) -> { Logger.getLogger(JsServerModuleEndPoint.class.getName()).log(Level.SEVERE, null, ex); }); }); } @OnClose public void sessionClosed(Session websocketSession) throws Exception { PlatypusServerCore platypusCore = lookupPlaypusServerCore(); in(platypusCore, websocketSession, (com.eas.server.Session aSession) -> { Logger.getLogger(JsServerModuleEndPoint.class.getName()).log(Level.FINE, "WebSocket container OnClose {0}.", aSession.getId()); JSObject closeEvent = Scripts.getSpace().makeObj(); closeEvent.setMember("wasClean", true); closeEvent.setMember("code", CloseReason.CloseCodes.NORMAL_CLOSURE.getCode()); closeEvent.setMember("reason", ""); closeEvent.setMember("id", websocketSession.getId()); platypusCore.executeMethod(moduleName, WS_ON_CLOSE, new Object[]{closeEvent}, true, (Object aResult) -> { com.eas.server.Session session = SessionManager.Singleton.instance.remove(wasPlatypusSessionId); Logger.getLogger(JsServerModuleEndPoint.class.getName()).log(Level.INFO, "WebSocket platypus session closed. Session id: {0}", session.getId()); Logger.getLogger(JsServerModuleEndPoint.class.getName()).log(Level.FINE, "{0} method of {1} module called successfully.", new Object[]{WS_ON_CLOSE, moduleName}); }, (Exception ex) -> { com.eas.server.Session session = SessionManager.Singleton.instance.remove(wasPlatypusSessionId); Logger.getLogger(JsServerModuleEndPoint.class.getName()).log(Level.INFO, "WebSocket platypus session closed. Session id: {0}", session.getId()); Logger.getLogger(JsServerModuleEndPoint.class.getName()).log(Level.SEVERE, null, ex); }); }); } protected PlatypusServerCore lookupPlaypusServerCore() throws IllegalStateException, Exception { PlatypusServerCore serverCore = PlatypusHttpServlet.getCore(); if (serverCore == null) { throw new IllegalStateException("Platypus server core is not initialized"); } return serverCore; } }