org.cometd.websocket.client.JettyWebSocketTransport Maven / Gradle / Ivy
/*
* Copyright (c) 2008-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.cometd.websocket.client;
import java.io.IOException;
import java.net.ConnectException;
import java.net.SocketTimeoutException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.channels.UnresolvedAddressException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ScheduledExecutorService;
import org.cometd.bayeux.Message.Mutable;
import org.cometd.client.transport.ClientTransport;
import org.cometd.client.transport.TransportListener;
import org.cometd.common.TransportException;
import org.eclipse.jetty.util.component.ContainerLifeCycle;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.UpgradeException;
import org.eclipse.jetty.websocket.api.UpgradeRequest;
import org.eclipse.jetty.websocket.api.UpgradeResponse;
import org.eclipse.jetty.websocket.api.WebSocketListener;
import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
import org.eclipse.jetty.websocket.client.WebSocketClient;
import org.eclipse.jetty.websocket.client.io.UpgradeListener;
public class JettyWebSocketTransport extends AbstractWebSocketTransport implements UpgradeListener
{
private final WebSocketClient _webSocketClient;
private boolean _webSocketSupported;
private boolean _webSocketConnected;
public JettyWebSocketTransport(Map options, ScheduledExecutorService scheduler, WebSocketClient webSocketClient)
{
this(null, options, scheduler, webSocketClient);
}
public JettyWebSocketTransport(String url, Map options, ScheduledExecutorService scheduler, WebSocketClient webSocketClient)
{
super(url, options, scheduler);
_webSocketClient = webSocketClient;
_webSocketSupported = true;
}
@Override
public boolean accept(String version)
{
return _webSocketSupported;
}
@Override
public void init()
{
super.init();
_webSocketClient.setConnectTimeout(getConnectTimeout());
_webSocketClient.getPolicy().setIdleTimeout(getIdleTimeout());
int maxMessageSize = getOption(MAX_MESSAGE_SIZE_OPTION, _webSocketClient.getPolicy().getMaxTextMessageSize());
_webSocketClient.getPolicy().setMaxTextMessageSize(maxMessageSize);
_webSocketClient.setCookieStore(getCookieStore());
_webSocketSupported = true;
_webSocketConnected = false;
}
protected Delegate connect(String uri, TransportListener listener, List messages)
{
try
{
if (logger.isDebugEnabled())
logger.debug("Opening websocket session to {}", uri);
_webSocketClient.setConnectTimeout(getConnectTimeout());
_webSocketClient.getPolicy().setIdleTimeout(getIdleTimeout());
ClientUpgradeRequest request = new ClientUpgradeRequest();
String protocol = getProtocol();
if (protocol != null)
request.setSubProtocols(protocol);
Delegate delegate = connect(_webSocketClient, request, uri);
_webSocketConnected = true;
return delegate;
}
catch (ConnectException | SocketTimeoutException | UnresolvedAddressException x)
{
// Cannot connect, assume the server supports WebSocket until proved otherwise
listener.onFailure(x, messages);
}
catch (UpgradeException x)
{
_webSocketSupported = false;
Map failure = new HashMap<>(2);
failure.put("websocketCode", 1002);
failure.put("httpCode", x.getResponseStatusCode());
listener.onFailure(new TransportException(x, failure), messages);
}
catch (Throwable x)
{
_webSocketSupported = isStickyReconnect() && _webSocketConnected;
listener.onFailure(x, messages);
}
return null;
}
protected Delegate connect(WebSocketClient client, ClientUpgradeRequest request, String uri) throws IOException, InterruptedException
{
try
{
Delegate delegate = newDelegate();
client.connect(delegate, new URI(uri), request, this).get();
return delegate;
}
catch (ExecutionException x)
{
Throwable cause = x.getCause();
if (cause instanceof RuntimeException)
throw (RuntimeException)cause;
if (cause instanceof IOException)
throw (IOException)cause;
throw new IOException(cause);
}
catch (URISyntaxException x)
{
throw new IOException(x);
}
}
protected Delegate newDelegate()
{
return new JettyWebSocketDelegate();
}
@Override
public void onHandshakeRequest(UpgradeRequest request)
{
}
@Override
public void onHandshakeResponse(UpgradeResponse response)
{
storeCookies(response.getHeaders());
}
protected class JettyWebSocketDelegate extends Delegate implements WebSocketListener
{
private Session _session;
@Override
public void onWebSocketConnect(Session session)
{
synchronized (this)
{
_session = session;
}
if (logger.isDebugEnabled())
logger.debug("Opened websocket session {}", session);
}
@Override
public void onWebSocketClose(int closeCode, String reason)
{
onClose(closeCode, reason);
}
@Override
public void onWebSocketText(String data)
{
onData(data);
}
@Override
public void onWebSocketBinary(byte[] payload, int offset, int len)
{
}
@Override
public void onWebSocketError(Throwable failure)
{
failMessages(failure);
}
@Override
public void send(String content)
{
Session session;
synchronized (this)
{
session = _session;
}
try
{
if (session == null)
throw new IOException("Unconnected");
// Blocking async sends for the client to allow concurrent sends.
session.getRemote().sendStringByFuture(content).get();
}
catch (Throwable x)
{
fail(x, "Exception");
}
}
@Override
protected void shutdown(String reason)
{
Session session;
synchronized (this)
{
session = _session;
close();
}
if (session != null)
{
if (logger.isDebugEnabled())
logger.debug("Closing websocket session {}", session);
session.close(1000, reason);
}
}
@Override
protected boolean isOpen()
{
synchronized (this)
{
return _session != null;
}
}
@Override
protected void close()
{
synchronized (this)
{
_session = null;
}
}
}
public static class Factory extends ContainerLifeCycle implements ClientTransport.Factory
{
private final WebSocketClient wsClient;
public Factory(WebSocketClient wsClient)
{
this.wsClient = wsClient;
addBean(wsClient);
}
@Override
public ClientTransport newClientTransport(String url, Map options)
{
ScheduledExecutorService scheduler = (ScheduledExecutorService)options.get(ClientTransport.SCHEDULER_OPTION);
return new JettyWebSocketTransport(url, options, scheduler, wsClient);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy