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}