Package org.cometd.client.transport

Source Code of org.cometd.client.transport.LongPollingTransport$Factory

/*
* 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.client.transport;

import java.io.IOException;
import java.net.CookieManager;
import java.net.CookiePolicy;
import java.net.HttpCookie;
import java.net.URI;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.cometd.bayeux.Channel;
import org.cometd.bayeux.Message;
import org.cometd.common.TransportException;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.api.Response;
import org.eclipse.jetty.client.api.Result;
import org.eclipse.jetty.client.util.BufferingResponseListener;
import org.eclipse.jetty.client.util.StringContentProvider;
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.util.component.ContainerLifeCycle;

public class LongPollingTransport extends HttpClientTransport
{
    public static final String NAME = "long-polling";
    public static final String PREFIX = "long-polling.json";
    public static final String MAX_BUFFER_SIZE_OPTION = "maxBufferSize";

    private final HttpClient _httpClient;
    private final List<Request> _requests = new ArrayList<>();
    private volatile boolean _aborted;
    private volatile int _maxBufferSize;
    private volatile boolean _appendMessageType;
    private volatile CookieManager _cookieManager;
    private volatile Map<String, Object> _advice;

    public LongPollingTransport(Map<String, Object> options, HttpClient httpClient)
    {
        this(null, options, httpClient);
    }

    public LongPollingTransport(String url, Map<String, Object> options, HttpClient httpClient)
    {
        super(NAME, url, options);
        _httpClient = httpClient;
        setOptionPrefix(PREFIX);
    }

    @Override
    public boolean accept(String bayeuxVersion)
    {
        return true;
    }

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

        _aborted = false;

        long defaultMaxNetworkDelay = _httpClient.getIdleTimeout();
        if (defaultMaxNetworkDelay <= 0)
            defaultMaxNetworkDelay = 10000;
        setMaxNetworkDelay(defaultMaxNetworkDelay);

        _maxBufferSize = getOption(MAX_BUFFER_SIZE_OPTION, 1024 * 1024);

        Pattern uriRegexp = Pattern.compile("(^https?://(((\\[[^\\]]+\\])|([^:/\\?#]+))(:(\\d+))?))?([^\\?#]*)(.*)?");
        Matcher uriMatcher = uriRegexp.matcher(getURL());
        if (uriMatcher.matches())
        {
            String afterPath = uriMatcher.group(9);
            _appendMessageType = afterPath == null || afterPath.trim().length() == 0;
        }
        _cookieManager = new CookieManager(getCookieStore(), CookiePolicy.ACCEPT_ALL);
    }

    @Override
    public void abort()
    {
        List<Request> requests = new ArrayList<>();
        synchronized (this)
        {
            _aborted = true;
            requests.addAll(_requests);
            _requests.clear();
        }
        for (Request request : requests)
        {
            request.abort(new Exception("Transport " + this + " aborted"));
        }
    }

    @Override
    public void send(final TransportListener listener, final List<Message.Mutable> messages)
    {
        String url = getURL();
        final URI uri = URI.create(url);
        if (_appendMessageType && messages.size() == 1)
        {
            Message.Mutable message = messages.get(0);
            if (message.isMeta())
            {
                String type = message.getChannel().substring(Channel.META.length());
                if (url.endsWith("/"))
                    url = url.substring(0, url.length() - 1);
                url += type;
            }
        }

        final Request request = _httpClient.newRequest(url).method(HttpMethod.POST);
        request.header(HttpHeader.CONTENT_TYPE.asString(), "application/json;charset=UTF-8");

        StringBuilder builder = new StringBuilder();
        for (HttpCookie cookie : getCookieStore().get(uri))
        {
            builder.setLength(0);
            builder.append(cookie.getName()).append("=").append(cookie.getValue());
            request.header(HttpHeader.COOKIE.asString(), builder.toString());
        }

        request.content(new StringContentProvider(generateJSON(messages)));

        customize(request);

        synchronized (this)
        {
            if (_aborted)
                throw new IllegalStateException("Aborted");
            _requests.add(request);
        }

        request.listener(new Request.Listener.Adapter()
        {
            @Override
            public void onHeaders(Request request)
            {
                listener.onSending(messages);
            }
        });

        long maxNetworkDelay = getMaxNetworkDelay();
        if (messages.size() == 1)
        {
            Message.Mutable message = messages.get(0);
            if (Channel.META_CONNECT.equals(message.getChannel()))
            {
                Map<String, Object> advice = message.getAdvice();
                if (advice == null)
                    advice = _advice;
                if (advice != null)
                {
                    Object timeout = advice.get("timeout");
                    if (timeout instanceof Number)
                        maxNetworkDelay += ((Number)timeout).longValue();
                    else if (timeout != null)
                        maxNetworkDelay += Long.parseLong(timeout.toString());
                }
            }
        }
        // Set the idle timeout for this request larger than the total timeout
        // so there are no races between the two timeouts
        request.idleTimeout(maxNetworkDelay * 2, TimeUnit.MILLISECONDS);
        request.timeout(maxNetworkDelay, TimeUnit.MILLISECONDS);
        request.send(new BufferingResponseListener(_maxBufferSize)
        {
            @Override
            public boolean onHeader(Response response, HttpField field)
            {
                HttpHeader header = field.getHeader();
                if (header != null && (header == HttpHeader.SET_COOKIE || header == HttpHeader.SET_COOKIE2))
                {
                    // We do not allow cookies to be handled by HttpClient, since one
                    // HttpClient instance is shared by multiple BayeuxClient instances.
                    // Instead, we store the cookies in the BayeuxClient instance.
                    Map<String, List<String>> cookies = new HashMap<>(1);
                    cookies.put(field.getName(), Collections.singletonList(field.getValue()));
                    storeCookies(uri, cookies);
                    return false;
                }
                return true;
            }

            private void storeCookies(URI uri, Map<String, List<String>> cookies)
            {
                try
                {
                    _cookieManager.put(uri, cookies);
                }
                catch (IOException x)
                {
                    if (logger.isDebugEnabled())
                        logger.debug("", x);
                }
            }

            @Override
            public void onComplete(Result result)
            {
                synchronized (LongPollingTransport.this)
                {
                    _requests.remove(result.getRequest());
                }

                if (result.isFailed())
                {
                    listener.onFailure(result.getFailure(), messages);
                    return;
                }

                Response response = result.getResponse();
                int status = response.getStatus();
                if (status == HttpStatus.OK_200)
                {
                    String content = getContentAsString();
                    if (content != null && content.length() > 0)
                    {
                        try
                        {
                            List<Message.Mutable> messages = parseMessages(content);
                            if (logger.isDebugEnabled())
                                logger.debug("Received messages {}", messages);
                            for (Message.Mutable message : messages)
                            {
                                if (message.isSuccessful() && Channel.META_CONNECT.equals(message.getChannel()))
                                {
                                    Map<String, Object> advice = message.getAdvice();
                                    if (advice != null && advice.get("timeout") != null)
                                        _advice = advice;
                                }
                            }
                            listener.onMessages(messages);
                        }
                        catch (ParseException x)
                        {
                            listener.onFailure(x, messages);
                        }
                    }
                    else
                    {
                        Map<String, Object> failure = new HashMap<>(2);
                        // Convert the 200 into 204 (no content)
                        failure.put("httpCode", 204);
                        TransportException x = new TransportException(failure);
                        listener.onFailure(x, messages);
                    }
                }
                else
                {
                    Map<String, Object> failure = new HashMap<>(2);
                    failure.put("httpCode", status);
                    TransportException x = new TransportException(failure);
                    listener.onFailure(x, messages);
                }
            }
        });
    }

    protected void customize(Request request)
    {
    }

    public static class Factory extends ContainerLifeCycle implements ClientTransport.Factory
    {
        private final HttpClient httpClient;

        public Factory(HttpClient httpClient)
        {
            this.httpClient = httpClient;
            addBean(httpClient);
        }

        @Override
        public ClientTransport newClientTransport(String url, Map<String, Object> options)
        {
            return new LongPollingTransport(url, options, httpClient);
        }
    }
}
TOP

Related Classes of org.cometd.client.transport.LongPollingTransport$Factory

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.