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}