001 package org.granite.gravity.glassfish;
002
003 import java.util.Collections;
004 import java.util.List;
005 import java.util.regex.Pattern;
006
007 import javax.servlet.ServletContext;
008
009 import org.granite.context.GraniteContext;
010 import org.granite.gravity.Gravity;
011 import org.granite.logging.Logger;
012 import org.granite.messaging.webapp.ServletGraniteContext;
013
014 import com.sun.grizzly.tcp.Request;
015 import com.sun.grizzly.websockets.WebSocket;
016 import com.sun.grizzly.websockets.WebSocketApplication;
017
018 import flex.messaging.messages.CommandMessage;
019 import flex.messaging.messages.Message;
020
021
022 public class GlassFishWebSocketApplication extends WebSocketApplication {
023
024 private static final Logger log = Logger.getLogger(GlassFishWebSocketApplication.class);
025
026 private final ServletContext servletContext;
027 private final Gravity gravity;
028 private final Pattern mapping;
029
030
031 public GlassFishWebSocketApplication(ServletContext servletContext, Gravity gravity, String mapping) {
032 this.servletContext = servletContext;
033 this.gravity = gravity;
034 this.mapping = Pattern.compile(".*" + mapping.replace("*", ".*") + "$");
035 }
036
037 @Override
038 public List<String> getSupportedProtocols(List<String> subProtocol) {
039 if (subProtocol.contains("org.granite.gravity"))
040 return Collections.singletonList("org.granite.gravity");
041 return Collections.emptyList();
042 }
043
044 @Override
045 public boolean isApplicationRequest(Request request) {
046 final String uri = request.requestURI().toString();
047 if (!mapping.matcher(uri).matches())
048 return false;
049
050 request.getParameters().handleQueryParameters(); // Force parse of query parameters
051 String connectMessageId = request.getHeader("connectId");
052 if (connectMessageId == null && request.getParameters().getParameter("connectId") != null)
053 connectMessageId = request.getParameters().getParameter("connectId");
054 String clientId = request.getHeader("GDSClientId") != null ? request.getHeader("GDSClientId") : request.getParameters().getParameter("GDSClientId");
055 String sessionId = null;
056
057 for (int i = 0; i < request.getCookies().getCookieCount(); i++) {
058 if ("JSESSIONID".equals(request.getCookies().getCookie(i).getName())) {
059 sessionId = request.getCookies().getCookie(i).getValue().getString();
060 break;
061 }
062 }
063 String clientType = null;
064 if (request.getHeader("GDSClientType") != null)
065 clientType = request.getHeader("GDSClientType");
066 if (clientType == null && request.getParameters().getParameter("GDSClientType") != null)
067 clientType = request.getParameters().getParameter("GDSClientType");
068
069 // Utterly hackish and ugly: we create the thread local here because there is no other way to access the request
070 // It will be cleared in onConnect which executes later in the same thread
071 ServletGraniteContext graniteContext = ServletGraniteContext.createThreadInstance(gravity.getGraniteConfig(), gravity.getServicesConfig(),
072 servletContext, sessionId, clientType);
073 if (connectMessageId != null)
074 graniteContext.getRequest().setAttribute("connectId", connectMessageId);
075 if (clientId != null)
076 graniteContext.getRequest().setAttribute("clientId", clientId);
077
078 return true;
079 }
080
081 @Override
082 public void onConnect(WebSocket websocket) {
083 GlassFishWebSocketChannelFactory channelFactory = new GlassFishWebSocketChannelFactory(gravity);
084
085 try {
086 log.info("WebSocket connection");
087 ServletGraniteContext graniteContext = (ServletGraniteContext)GraniteContext.getCurrentInstance();
088
089 String connectMessageId = (String)graniteContext.getRequest().getAttribute("connectId");
090 String clientId = (String)graniteContext.getRequest().getAttribute("clientId");
091
092 CommandMessage pingMessage = new CommandMessage();
093 pingMessage.setMessageId(connectMessageId != null ? connectMessageId : "OPEN_CONNECTION");
094 pingMessage.setOperation(CommandMessage.CLIENT_PING_OPERATION);
095 if (clientId != null)
096 pingMessage.setClientId(clientId);
097
098 Message ackMessage = gravity.handleMessage(channelFactory, pingMessage);
099
100 GlassFishWebSocketChannel channel = gravity.getChannel(channelFactory, (String)ackMessage.getClientId());
101 if (!ackMessage.getClientId().equals(clientId))
102 channel.setConnectAckMessage(ackMessage);
103 channel.setWebSocket(websocket);
104 }
105 finally {
106 GraniteContext.release();
107 }
108 }
109 }