/**
* GRANITE DATA SERVICES
* Copyright (C) 2006-2015 GRANITE DATA SERVICES S.A.S.
*
* This file is part of the Granite Data Services Platform.
*
* Granite Data Services is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* Granite Data Services is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
* General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA, or see <http://www.gnu.org/licenses/>.
*/
package org.granite.client.messaging.transport.jetty;
import java.io.IOException;
import java.net.URI;
import java.util.concurrent.TimeoutException;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.websocket.WebSocket.Connection;
import org.eclipse.jetty.websocket.WebSocket.OnBinaryMessage;
import org.eclipse.jetty.websocket.WebSocketClient;
import org.eclipse.jetty.websocket.WebSocketClientFactory;
import org.granite.client.messaging.channel.Channel;
import org.granite.client.messaging.transport.*;
import org.granite.client.messaging.transport.websocket.AbstractWebSocketTransport;
import org.granite.logging.Logger;
/**
* @author William DRAI
*/
public class JettyWebSocketTransport extends AbstractWebSocketTransport<Connection> {
private static final Logger log = Logger.getLogger(JettyWebSocketTransport.class);
private WebSocketClientFactory webSocketClientFactory = new WebSocketClientFactory();
public SslContextFactory getSetSslContextFactory() {
return webSocketClientFactory.getSslContextFactory();
}
@Override
public synchronized boolean start() {
if (isStarted())
return true;
log.info("Starting Jetty WebSocketClient transport...");
try {
webSocketClientFactory.setBufferSize(4096);
webSocketClientFactory.start();
final long timeout = System.currentTimeMillis() + 10000L; // 10sec.
while (!webSocketClientFactory.isStarted()) {
if (System.currentTimeMillis() > timeout)
throw new TimeoutException("Jetty WebSocketFactory start process too long");
Thread.sleep(100);
}
log.info("Jetty WebSocketClient transport started.");
return true;
}
catch (Exception e) {
webSocketClientFactory = null;
getStatusHandler().handleException(new TransportException("Could not start Jetty WebSocketFactory", e));
log.error(e, "Jetty WebSocketClient transport failed to start.");
return false;
}
}
public synchronized boolean isStarted() {
return webSocketClientFactory != null && webSocketClientFactory.isStarted();
}
public void connect(final Channel channel, final TransportMessage transportMessage) {
URI uri = channel.getUri();
try {
WebSocketClient webSocketClient = webSocketClientFactory.newWebSocketClient();
webSocketClient.setMaxIdleTime(getMaxIdleTime());
webSocketClient.setMaxTextMessageSize(1024);
webSocketClient.setMaxBinaryMessageSize(getMaxMessageSize());
webSocketClient.setProtocol("org.granite.gravity." + transportMessage.getContentType().substring("application/x-".length()));
if (transportMessage.getSessionId() != null)
webSocketClient.getCookies().put("JSESSIONID", transportMessage.getSessionId());
String u = uri.toString();
u += "?connectId=" + transportMessage.getId() + "&GDSClientType=" + transportMessage.getClientType();
if (transportMessage.getClientId() != null)
u += "&GDSClientId=" + transportMessage.getClientId();
else if (channel.getClientId() != null)
u += "&GDSClientId=" + channel.getClientId();
log.info("Connecting to websocket %s protocol %s sessionId %s", u, webSocketClient.getProtocol(), transportMessage.getSessionId());
webSocketClient.open(new URI(u), new WebSocketHandler(channel));
}
catch (Exception e) {
getStatusHandler().handleException(new TransportException("Could not connect to uri " + channel.getUri(), e));
}
}
@Override
public synchronized void stop() {
if (webSocketClientFactory == null)
return;
log.info("Stopping Jetty WebSocketClient transport...");
super.stop();
try {
setStopping(true);
webSocketClientFactory.stop();
}
catch (Exception e) {
getStatusHandler().handleException(new TransportException("Could not stop Jetty WebSocketFactory", e));
log.error(e, "Jetty WebSocketClient failed to stop properly.");
}
finally {
setStopping(false);
}
log.info("Jetty WebSocketClient transport stopped.");
}
private static class JettyTransportData extends TransportData<Connection> {
private Connection connection = null;
@Override
public void connect(Connection connection) {
this.connection = connection;
}
@Override
public boolean isConnected() {
return connection != null;
}
@Override
public void disconnect() {
this.connection = null;
}
@Override
public void sendBytes(byte[] data) throws IOException {
this.connection.sendMessage(data, 0, data.length);
}
}
public TransportData<Connection> newTransportData() {
return new JettyTransportData();
}
private class WebSocketHandler implements OnBinaryMessage {
private final Channel channel;
public WebSocketHandler(Channel channel) {
this.channel = channel;
}
@Override
public void onOpen(Connection connection) {
onConnect(channel, connection);
}
@Override
public void onMessage(byte[] data, int offset, int length) {
onBinaryMessage(channel, data, offset, length);
}
@Override
public void onClose(int closeCode, String message) {
JettyWebSocketTransport.this.onClose(channel, closeCode, message);
}
}
}