001package org.granite.gravity.glassfish; 002 003import java.util.Collections; 004import java.util.List; 005import java.util.regex.Pattern; 006 007import javax.servlet.ServletContext; 008 009import org.granite.context.GraniteContext; 010import org.granite.gravity.Gravity; 011import org.granite.logging.Logger; 012import org.granite.messaging.webapp.ServletGraniteContext; 013 014import com.sun.grizzly.tcp.Request; 015import com.sun.grizzly.websockets.WebSocket; 016import com.sun.grizzly.websockets.WebSocketApplication; 017 018import flex.messaging.messages.CommandMessage; 019import flex.messaging.messages.Message; 020 021 022public 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}