001/**
002 *   GRANITE DATA SERVICES
003 *   Copyright (C) 2006-2013 GRANITE DATA SERVICES S.A.S.
004 *
005 *   This file is part of the Granite Data Services Platform.
006 *
007 *   Granite Data Services is free software; you can redistribute it and/or
008 *   modify it under the terms of the GNU Lesser General Public
009 *   License as published by the Free Software Foundation; either
010 *   version 2.1 of the License, or (at your option) any later version.
011 *
012 *   Granite Data Services is distributed in the hope that it will be useful,
013 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
014 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
015 *   General Public License for more details.
016 *
017 *   You should have received a copy of the GNU Lesser General Public
018 *   License along with this library; if not, write to the Free Software
019 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
020 *   USA, or see <http://www.gnu.org/licenses/>.
021 */
022package org.granite.gravity.glassfish;
023
024import java.util.Collections;
025import java.util.List;
026import java.util.regex.Pattern;
027
028import javax.servlet.ServletContext;
029import javax.servlet.http.HttpServletRequest;
030import javax.servlet.http.HttpSession;
031
032import com.sun.grizzly.websockets.*;
033import org.granite.context.GraniteContext;
034import org.granite.gravity.Gravity;
035import org.granite.logging.Logger;
036import org.granite.messaging.webapp.ServletGraniteContext;
037import org.granite.util.ContentType;
038
039import com.sun.grizzly.tcp.Request;
040
041import flex.messaging.messages.CommandMessage;
042import flex.messaging.messages.Message;
043
044
045public class GlassFishWebSocketApplication extends WebSocketApplication {
046        
047        private static final Logger log = Logger.getLogger(GlassFishWebSocketApplication.class);
048        
049        private final ServletContext servletContext;
050        private final Gravity gravity;
051        private final Pattern mapping;
052    private String protocol;
053
054
055        public GlassFishWebSocketApplication(ServletContext servletContext, Gravity gravity, String mapping) {
056                this.servletContext = servletContext;
057                this.gravity = gravity;
058                this.mapping = Pattern.compile(".*" + mapping.replace("*", ".*") + "$");
059        }
060
061        @Override
062        public List<String> getSupportedProtocols(List<String> subProtocols) {
063        for (String subProtocol : subProtocols) {
064                    if (subProtocol.startsWith("org.granite.gravity")) {
065                this.protocol = subProtocol;
066                            return Collections.singletonList(subProtocol);
067            }
068        }
069                return Collections.emptyList();
070        }
071
072        @Override
073        public boolean isApplicationRequest(Request request) {
074        final String uri = request.requestURI().toString();
075        return mapping.matcher(uri).matches();
076        }
077
078        @Override
079    public void onConnect(WebSocket websocket) {
080        if (!(websocket instanceof DefaultWebSocket))
081            throw new IllegalStateException("Only DefaultWebSocket supported");
082
083                GlassFishWebSocketChannelFactory channelFactory = new GlassFishWebSocketChannelFactory(gravity);
084        HttpServletRequest request = ((DefaultWebSocket)websocket).getRequest();
085                
086                try {
087            String connectMessageId = request.getHeader("connectId") != null ? request.getHeader("connectId") : request.getParameter("connectId");
088            String clientId = request.getHeader("GDSClientId") != null ? request.getHeader("GDSClientId") : request.getParameter("GDSClientId");
089            String clientType = request.getHeader("GDSClientType") != null ? request.getHeader("GDSClientType") : request.getParameter("GDSClientType");
090            String sessionId = null;
091            HttpSession session = request.getSession("true".equals(servletContext.getInitParameter("org.granite.gravity.websocket.forceCreateSession")));
092            if (session != null) {
093                ServletGraniteContext.createThreadInstance(gravity.getGraniteConfig(), gravity.getServicesConfig(),
094                        this.servletContext, session, clientType);
095
096                sessionId = session.getId();
097            }
098            else if (request.getCookies() != null) {
099                for (int i = 0; i < request.getCookies().length; i++) {
100                    if ("JSESSIONID".equals(request.getCookies()[i].getName())) {
101                        sessionId = request.getCookies()[i].getValue();
102                        break;
103                    }
104                }
105                ServletGraniteContext.createThreadInstance(gravity.getGraniteConfig(), gravity.getServicesConfig(),
106                        this.servletContext, sessionId, clientType);
107            }
108            else {
109                ServletGraniteContext.createThreadInstance(gravity.getGraniteConfig(), gravity.getServicesConfig(),
110                        this.servletContext, (String)null, clientType);
111            }
112
113            log.info("WebSocket connection started %s clientId %s sessionId %s", protocol, clientId, sessionId);
114
115            CommandMessage pingMessage = new CommandMessage();
116                        pingMessage.setMessageId(connectMessageId != null ? connectMessageId : "OPEN_CONNECTION");
117                        pingMessage.setOperation(CommandMessage.CLIENT_PING_OPERATION);
118                        if (clientId != null)
119                                pingMessage.setClientId(clientId);
120                        
121                        Message ackMessage = gravity.handleMessage(channelFactory, pingMessage);
122            if (sessionId != null)
123                ackMessage.setHeader("JSESSIONID", sessionId);
124
125                        GlassFishWebSocketChannel channel = gravity.getChannel(channelFactory, (String)ackMessage.getClientId());
126            channel.setSession(session);
127
128            String ctype = request.getContentType();
129            if (ctype == null && protocol.length() > "org.granite.gravity".length())
130                ctype = "application/x-" + protocol.substring("org.granite.gravity.".length());
131
132                        ContentType contentType = ContentType.forMimeType(ctype);
133                        if (contentType == null) {
134                                log.warn("No (or unsupported) content type in request: %s", request.getContentType());
135                                contentType = ContentType.AMF;
136                        }
137                        channel.setContentType(contentType);
138
139                        if (!ackMessage.getClientId().equals(clientId))
140                                channel.setConnectAckMessage(ackMessage);
141                        
142                        channel.setWebSocket(websocket);
143                }
144                finally {
145                        GraniteContext.release();
146                }
147    }
148}