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     */
022    package org.granite.gravity.glassfish;
023    
024    import java.util.Collections;
025    import java.util.List;
026    import java.util.regex.Pattern;
027    
028    import javax.servlet.ServletContext;
029    import javax.servlet.http.HttpServletRequest;
030    import javax.servlet.http.HttpSession;
031    
032    import com.sun.grizzly.websockets.*;
033    import org.granite.context.GraniteContext;
034    import org.granite.gravity.Gravity;
035    import org.granite.logging.Logger;
036    import org.granite.messaging.webapp.ServletGraniteContext;
037    import org.granite.util.ContentType;
038    
039    import com.sun.grizzly.tcp.Request;
040    
041    import flex.messaging.messages.CommandMessage;
042    import flex.messaging.messages.Message;
043    
044    
045    public 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    }