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    }