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 }