Package org.cometd.websocket.client

Source Code of org.cometd.websocket.client.WebSocketTransport$Configurator

/*
* 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.CookieStore;
import java.net.HttpCookie;
import java.net.SocketTimeoutException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.channels.UnresolvedAddressException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ScheduledExecutorService;
import javax.websocket.ClientEndpointConfig;
import javax.websocket.CloseReason;
import javax.websocket.ContainerProvider;
import javax.websocket.DeploymentException;
import javax.websocket.Endpoint;
import javax.websocket.EndpointConfig;
import javax.websocket.HandshakeResponse;
import javax.websocket.MessageHandler;
import javax.websocket.Session;
import javax.websocket.WebSocketContainer;

import org.cometd.bayeux.Message.Mutable;
import org.cometd.client.transport.ClientTransport;
import org.cometd.client.transport.TransportListener;
import org.eclipse.jetty.util.component.ContainerLifeCycle;

public class WebSocketTransport extends AbstractWebSocketTransport
{
    private final WebSocketContainer _webSocketContainer;
    private boolean _webSocketSupported;
    private boolean _webSocketConnected;

    public WebSocketTransport(Map<String, Object> options, ScheduledExecutorService scheduler, WebSocketContainer webSocketContainer)
    {
        this(null, options, scheduler, webSocketContainer);
    }

    public WebSocketTransport(String url, Map<String, Object> options, ScheduledExecutorService scheduler, WebSocketContainer webSocketContainer)
    {
        super(url, options, scheduler);
        _webSocketContainer = webSocketContainer;
        _webSocketSupported = true;
    }

    @Override
    public boolean accept(String version)
    {
        return _webSocketSupported;
    }

    @Override
    public void init()
    {
        super.init();

        // JSR 356 does not expose a way to set the connect timeout - ignored
        _webSocketContainer.setDefaultMaxSessionIdleTimeout(getIdleTimeout());
        int maxMessageSize = getOption(MAX_MESSAGE_SIZE_OPTION, _webSocketContainer.getDefaultMaxTextMessageBufferSize());
        _webSocketContainer.setDefaultMaxTextMessageBufferSize(maxMessageSize);

        _webSocketSupported = true;
        _webSocketConnected = false;
    }

    protected Delegate connect(String uri, TransportListener listener, List<Mutable> messages)
    {
        try
        {
            if (logger.isDebugEnabled())
                logger.debug("Opening websocket session to {}", uri);
            _webSocketContainer.setDefaultMaxSessionIdleTimeout(getIdleTimeout());
            ClientEndpointConfig.Configurator configurator = new Configurator();
            String protocol = getProtocol();
            ClientEndpointConfig config = ClientEndpointConfig.Builder.create()
                    .preferredSubprotocols(protocol == null ? null : Collections.singletonList(protocol))
                    .configurator(configurator).build();
            Delegate delegate = connect(_webSocketContainer, config, 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 (Throwable x)
        {
            _webSocketSupported = isStickyReconnect() && _webSocketConnected;
            listener.onFailure(x, messages);
        }
        return null;
    }

    protected Delegate connect(WebSocketContainer container, ClientEndpointConfig configuration, String uri) throws IOException
    {
        try
        {
            WebSocketDelegate delegate = newDelegate();
            container.connectToServer(delegate._endpoint, configuration, new URI(uri));
            return delegate;
        }
        catch (DeploymentException | URISyntaxException x)
        {
            throw new IOException(x);
        }
    }

    protected WebSocketDelegate newDelegate()
    {
        return new WebSocketDelegate();
    }

    protected class WebSocketDelegate extends Delegate implements MessageHandler.Whole<String>
    {
        private final Endpoint _endpoint = new WebSocketEndpoint();
        private Session _session;

        private void onOpen(Session session)
        {
            synchronized (this)
            {
                _session = session;
            }
            session.addMessageHandler(this);
            if (logger.isDebugEnabled())
                logger.debug("Opened websocket session {}", session);
        }

        @Override
        public void onMessage(String data)
        {
            onData(data);
        }

        @Override
        public void send(String content)
        {
            Session session;
            synchronized (this)
            {
                session = _session;
            }
            try
            {
                if (session == null)
                    throw new IOException("Unconnected");

                // Blocking sends for the client, using
                // AsyncRemote to allow concurrent sends.
                session.getAsyncRemote().sendText(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 {}", reason, session);
                try
                {
                    session.close(new CloseReason(CloseReason.CloseCodes.NORMAL_CLOSURE, reason));
                }
                catch (IOException x)
                {
                    logger.trace("Could not close websocket session " + session, x);
                }
            }
        }

        @Override
        protected boolean isOpen()
        {
            synchronized (this)
            {
                return _session != null;
            }
        }

        protected void close()
        {
            synchronized (this)
            {
                _session = null;
            }
        }

        private class WebSocketEndpoint extends Endpoint
        {
            @Override
            public void onOpen(Session session, EndpointConfig config)
            {
                WebSocketDelegate.this.onOpen(session);
            }

            @Override
            public void onClose(Session session, CloseReason closeReason)
            {
                WebSocketDelegate.this.onClose(closeReason.getCloseCode().getCode(), closeReason.getReasonPhrase());
            }

            @Override
            public void onError(Session session, Throwable failure)
            {
                failMessages(failure);
            }
        }
    }

    private class Configurator extends ClientEndpointConfig.Configurator
    {
        @Override
        public void beforeRequest(Map<String, List<String>> headers)
        {
            CookieStore cookieStore = getCookieStore();
            List<HttpCookie> cookies = cookieStore.get(URI.create(getURL()));
            if (!cookies.isEmpty())
            {
                List<String> cookieHeader = headers.get("Cookie");
                if (cookieHeader == null)
                    cookieHeader = headers.get("cookie");
                if (cookieHeader == null)
                    headers.put("Cookie", cookieHeader = new ArrayList<>());
                for (HttpCookie cookie : cookies)
                    cookieHeader.add(cookie.getName() + "=" + cookie.getValue());
            }
        }

        @Override
        public void afterResponse(HandshakeResponse hr)
        {
            Map<String, List<String>> headers = hr.getHeaders();

            storeCookies(headers);

            _webSocketSupported = false;
            // Must do case-insensitive search.
            for (String name : headers.keySet())
            {
                if (HandshakeResponse.SEC_WEBSOCKET_ACCEPT.equalsIgnoreCase(name))
                {
                    _webSocketSupported = true;
                    break;
                }
            }
        }
    }

    public static class Factory extends ContainerLifeCycle implements ClientTransport.Factory
    {
        private final WebSocketContainer container = ContainerProvider.getWebSocketContainer();

        public Factory()
        {
            // The WebSocketContainer is already started, so we must explicitly manage it.
            addBean(container, true);
        }

        @Override
        public ClientTransport newClientTransport(String url, Map<String, Object> options)
        {
            ScheduledExecutorService scheduler = (ScheduledExecutorService)options.get(ClientTransport.SCHEDULER_OPTION);
            return new WebSocketTransport(url, options, scheduler, container);
        }
    }
}
TOP

Related Classes of org.cometd.websocket.client.WebSocketTransport$Configurator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.