All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.jetty.websocket.WebSocketClient Maven / Gradle / Ivy

There is a newer version: 11.0.0.beta1
Show newest version
package org.eclipse.jetty.websocket;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ProtocolException;
import java.net.SocketAddress;
import java.net.URI;
import java.nio.channels.ByteChannel;
import java.nio.channels.SocketChannel;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import org.eclipse.jetty.util.log.Logger;


/* ------------------------------------------------------------ */
/**
 * 

{@link WebSocketClient} allows to create multiple connections to multiple destinations * that can speak the websocket protocol.

*

When creating websocket connections, {@link WebSocketClient} accepts a {@link WebSocket} * object (to receive events from the server), and returns a {@link WebSocket.Connection} to * send data to the server.

*

Example usage is as follows:

*
 *   WebSocketClientFactory factory = new WebSocketClientFactory();
 *   factory.start();
 *
 *   WebSocketClient client = factory.newWebSocketClient();
 *   // Configure the client
 *
 *   WebSocket.Connection connection = client.open(new URI("ws://127.0.0.1:8080/"), new WebSocket.OnTextMessage()
 *   {
 *     public void onOpen(Connection connection)
 *     {
 *       // open notification
 *     }
 *
 *     public void onClose(int closeCode, String message)
 *     {
 *       // close notification
 *     }
 *
 *     public void onMessage(String data)
 *     {
 *       // handle incoming message
 *     }
 *   }).get(5, TimeUnit.SECONDS);
 *
 *   connection.sendMessage("Hello World");
 * 
*/ public class WebSocketClient { private final static Logger __log = org.eclipse.jetty.util.log.Log.getLogger(WebSocketClient.class.getName()); private final WebSocketClientFactory _factory; private final Map _cookies=new ConcurrentHashMap(); private final List _extensions=new CopyOnWriteArrayList(); private String _origin; private String _protocol; private int _maxIdleTime=-1; private MaskGen _maskGen; private SocketAddress _bindAddress; /* ------------------------------------------------------------ */ /** *

Creates a WebSocketClient from a private WebSocketClientFactory.

*

This can be wasteful of resources if many clients are created.

* * @deprecated Use {@link WebSocketClientFactory#newWebSocketClient()} * @throws Exception if the private WebSocketClientFactory fails to start */ @Deprecated public WebSocketClient() throws Exception { _factory=new WebSocketClientFactory(); _factory.start(); _maskGen=_factory.getMaskGen(); } /* ------------------------------------------------------------ */ /** *

Creates a WebSocketClient with shared WebSocketClientFactory.

* * @param factory the shared {@link WebSocketClientFactory} */ public WebSocketClient(WebSocketClientFactory factory) { _factory=factory; _maskGen=_factory.getMaskGen(); } /* ------------------------------------------------------------ */ /** * @return The WebSocketClientFactory this client was created with. */ public WebSocketClientFactory getFactory() { return _factory; } /* ------------------------------------------------------------ */ /** * @return the address to bind the socket channel to * @see #setBindAddress(SocketAddress) */ public SocketAddress getBindAddress() { return _bindAddress; } /* ------------------------------------------------------------ */ /** * @param bindAddress the address to bind the socket channel to * @see #getBindAddress() */ public void setBindAddress(SocketAddress bindAddress) { this._bindAddress = bindAddress; } /* ------------------------------------------------------------ */ /** * @return The maxIdleTime in ms for connections opened by this client, * or -1 if the default from {@link WebSocketClientFactory#getSelectorManager()} is used. * @see #setMaxIdleTime(int) */ public int getMaxIdleTime() { return _maxIdleTime; } /* ------------------------------------------------------------ */ /** * @param maxIdleTime The max idle time in ms for connections opened by this client * @see #getMaxIdleTime() */ public void setMaxIdleTime(int maxIdleTime) { _maxIdleTime=maxIdleTime; } /* ------------------------------------------------------------ */ /** * @return The subprotocol string for connections opened by this client. * @see #setProtocol(String) */ public String getProtocol() { return _protocol; } /* ------------------------------------------------------------ */ /** * @param protocol The subprotocol string for connections opened by this client. * @see #getProtocol() */ public void setProtocol(String protocol) { _protocol = protocol; } /* ------------------------------------------------------------ */ /** * @return The origin URI of the client * @see #setOrigin(String) */ public String getOrigin() { return _origin; } /* ------------------------------------------------------------ */ /** * @param origin The origin URI of the client (eg "http://example.com") * @see #getOrigin() */ public void setOrigin(String origin) { _origin = origin; } /* ------------------------------------------------------------ */ /** *

Returns the map of the cookies that are sent during the initial HTTP handshake * that upgrades to the websocket protocol.

* @return The read-write cookie map */ public Map getCookies() { return _cookies; } /* ------------------------------------------------------------ */ /** * @return The list of websocket protocol extensions */ public List getExtensions() { return _extensions; } /* ------------------------------------------------------------ */ /** * @return the mask generator to use, or null if not mask generator should be used * @see #setMaskGen(MaskGen) */ public MaskGen getMaskGen() { return _maskGen; } /* ------------------------------------------------------------ */ /** * @param maskGen the mask generator to use, or null if not mask generator should be used * @see #getMaskGen() */ public void setMaskGen(MaskGen maskGen) { _maskGen = maskGen; } /* ------------------------------------------------------------ */ /** *

Opens a websocket connection to the URI and blocks until the connection is accepted or there is an error.

* * @param uri The URI to connect to. * @param websocket The {@link WebSocket} instance to handle incoming events. * @param maxConnectTime The interval to wait for a successful connection * @param units the units of the maxConnectTime * @return A {@link WebSocket.Connection} * @throws IOException if the connection fails * @throws InterruptedException if the thread is interrupted * @throws TimeoutException if the timeout elapses before the connection is completed * @see #open(URI, WebSocket) */ public WebSocket.Connection open(URI uri, WebSocket websocket,long maxConnectTime,TimeUnit units) throws IOException, InterruptedException, TimeoutException { try { return open(uri,websocket).get(maxConnectTime,units); } catch (ExecutionException e) { Throwable cause = e.getCause(); if (cause instanceof IOException) throw (IOException)cause; if (cause instanceof Error) throw (Error)cause; if (cause instanceof RuntimeException) throw (RuntimeException)cause; throw new RuntimeException(cause); } } /* ------------------------------------------------------------ */ /** *

Asynchronously opens a websocket connection and returns a {@link Future} to obtain the connection.

*

The caller must call {@link Future#get(long, TimeUnit)} if they wish to impose a connect timeout on the open.

* * @param uri The URI to connect to. * @param websocket The {@link WebSocket} instance to handle incoming events. * @return A {@link Future} to the {@link WebSocket.Connection} * @throws IOException if the connection fails * @see #open(URI, WebSocket, long, TimeUnit) */ public Future open(URI uri, WebSocket websocket) throws IOException { if (!_factory.isStarted()) throw new IllegalStateException("Factory !started"); String scheme=uri.getScheme(); if (!("ws".equalsIgnoreCase(scheme) || "wss".equalsIgnoreCase(scheme))) throw new IllegalArgumentException("Bad WebSocket scheme '"+scheme+"'"); if ("wss".equalsIgnoreCase(scheme)) throw new IOException("wss not supported"); SocketChannel channel = SocketChannel.open(); if (_bindAddress != null) channel.socket().bind(_bindAddress); channel.socket().setTcpNoDelay(true); int maxIdleTime = getMaxIdleTime(); InetSocketAddress address=new InetSocketAddress(uri.getHost(),uri.getPort()); final WebSocketFuture holder=new WebSocketFuture(websocket,uri,_protocol,_origin,_maskGen,maxIdleTime,_cookies,_extensions,channel); channel.configureBlocking(false); channel.connect(address); _factory.getSelectorManager().register( channel, holder); return holder; } /* ------------------------------------------------------------ */ /** The Future Websocket Connection. */ static class WebSocketFuture implements Future { final WebSocket _websocket; final URI _uri; final String _protocol; final String _origin; final MaskGen _maskGen; final int _maxIdleTime; final Map _cookies; final List _extensions; final CountDownLatch _done = new CountDownLatch(1); ByteChannel _channel; WebSocketConnection _connection; Throwable _exception; private WebSocketFuture(WebSocket websocket, URI uri, String protocol, String origin, MaskGen maskGen, int maxIdleTime, Map cookies,List extensions, ByteChannel channel) { _websocket=websocket; _uri=uri; _protocol=protocol; _origin=origin; _maskGen=maskGen; _maxIdleTime=maxIdleTime; _cookies=cookies; _extensions=extensions; _channel=channel; } public void onConnection(WebSocketConnection connection) { try { synchronized (this) { if (_channel!=null) _connection=connection; } if (_connection!=null) { if (_websocket instanceof WebSocket.OnFrame) ((WebSocket.OnFrame)_websocket).onHandshake((WebSocket.FrameConnection)connection.getConnection()); _websocket.onOpen(connection.getConnection()); } } finally { _done.countDown(); } } public void handshakeFailed(Throwable ex) { try { ByteChannel channel=null; synchronized (this) { if (_channel!=null) { channel=_channel; _channel=null; _exception=ex; } } if (channel!=null) { if (ex instanceof ProtocolException) closeChannel(channel,WebSocketConnectionD12.CLOSE_PROTOCOL,ex.getMessage()); else closeChannel(channel,WebSocketConnectionD12.CLOSE_NOCLOSE,ex.getMessage()); } } finally { _done.countDown(); } } public Map getCookies() { return _cookies; } public String getProtocol() { return _protocol; } public WebSocket getWebSocket() { return _websocket; } public URI getURI() { return _uri; } public int getMaxIdleTime() { return _maxIdleTime; } public String getOrigin() { return _origin; } public MaskGen getMaskGen() { return _maskGen; } public String toString() { return "[" + _uri + ","+_websocket+"]@"+hashCode(); } public boolean cancel(boolean mayInterruptIfRunning) { try { ByteChannel channel=null; synchronized (this) { if (_connection==null && _exception==null && _channel!=null) { channel=_channel; _channel=null; } } if (channel!=null) { closeChannel(channel,WebSocketConnectionD12.CLOSE_NOCLOSE,"cancelled"); return true; } return false; } finally { _done.countDown(); } } public boolean isCancelled() { synchronized (this) { return _channel==null && _connection==null; } } public boolean isDone() { synchronized (this) { return _connection!=null && _exception==null; } } public org.eclipse.jetty.websocket.WebSocket.Connection get() throws InterruptedException, ExecutionException { try { return get(Long.MAX_VALUE,TimeUnit.SECONDS); } catch(TimeoutException e) { throw new IllegalStateException("The universe has ended",e); } } public org.eclipse.jetty.websocket.WebSocket.Connection get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { _done.await(timeout,unit); ByteChannel channel=null; org.eclipse.jetty.websocket.WebSocket.Connection connection=null; Throwable exception; synchronized (this) { exception=_exception; if (_connection==null) { exception=_exception; channel=_channel; _channel=null; } else connection=_connection.getConnection(); } if (channel!=null) closeChannel(channel,WebSocketConnectionD12.CLOSE_NOCLOSE,"timeout"); if (exception!=null) throw new ExecutionException(exception); if (connection!=null) return connection; throw new TimeoutException(); } private void closeChannel(ByteChannel channel,int code, String message) { try { _websocket.onClose(code,message); } catch(Exception e) { __log.warn(e); } try { channel.close(); } catch(IOException e) { __log.debug(e); } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy