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

com.vaadin.client.communication.XhrConnection Maven / Gradle / Ivy

Go to download

Vaadin is a web application framework for Rich Internet Applications (RIA). Vaadin enables easy development and maintenance of fast and secure rich web applications with a stunning look and feel and a wide browser support. It features a server-side architecture with the majority of the logic running on the server. Ajax technology is used at the browser-side to ensure a rich and interactive user experience.

There is a newer version: 8.27.1
Show newest version
/*
 * Copyright 2000-2016 Vaadin Ltd.
 *
 * 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 com.vaadin.client.communication;

import java.util.logging.Logger;

import com.google.gwt.http.client.Request;
import com.google.gwt.http.client.RequestBuilder;
import com.google.gwt.http.client.RequestCallback;
import com.google.gwt.http.client.RequestException;
import com.google.gwt.http.client.Response;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.Window.ClosingEvent;
import com.google.gwt.user.client.Window.ClosingHandler;
import com.vaadin.client.ApplicationConnection;
import com.vaadin.client.ApplicationConnection.CommunicationHandler;
import com.vaadin.client.ApplicationConnection.RequestStartingEvent;
import com.vaadin.client.ApplicationConnection.ResponseHandlingEndedEvent;
import com.vaadin.client.ApplicationConnection.ResponseHandlingStartedEvent;
import com.vaadin.client.BrowserInfo;
import com.vaadin.client.Profiler;
import com.vaadin.client.Util;
import com.vaadin.client.ValueMap;
import com.vaadin.shared.ApplicationConstants;
import com.vaadin.shared.JsonConstants;
import com.vaadin.shared.ui.ui.UIConstants;
import com.vaadin.shared.util.SharedUtil;

import elemental.json.JsonObject;

/**
 * Provides a connection to the /UIDL url on the server and knows how to send
 * messages to that end point
 *
 * @since 7.6
 * @author Vaadin Ltd
 */
public class XhrConnection {

    private ApplicationConnection connection;

    /**
     * Webkit will ignore outgoing requests while waiting for a response to a
     * navigation event (indicated by a beforeunload event). When this happens,
     * we should keep trying to send the request every now and then until there
     * is a response or until it throws an exception saying that it is already
     * being sent.
     */
    private boolean webkitMaybeIgnoringRequests = false;

    public XhrConnection() {
        Window.addWindowClosingHandler(new ClosingHandler() {
            @Override
            public void onWindowClosing(ClosingEvent event) {
                webkitMaybeIgnoringRequests = true;
            }
        });
    }

    /**
     * Sets the application connection this instance is connected to. Called
     * internally by the framework.
     *
     * @param connection
     *            the application connection this instance is connected to
     */
    public void setConnection(ApplicationConnection connection) {
        this.connection = connection;

        connection.addHandler(ResponseHandlingEndedEvent.TYPE,
                new CommunicationHandler() {
                    @Override
                    public void onRequestStarting(RequestStartingEvent e) {
                    }

                    @Override
                    public void onResponseHandlingStarted(
                            ResponseHandlingStartedEvent e) {
                    }

                    @Override
                    public void onResponseHandlingEnded(
                            ResponseHandlingEndedEvent e) {
                        webkitMaybeIgnoringRequests = false;
                    }
                });

    }

    private static Logger getLogger() {
        return Logger.getLogger(XhrConnection.class.getName());
    }

    protected XhrResponseHandler createResponseHandler() {
        return new XhrResponseHandler();
    }

    public class XhrResponseHandler implements RequestCallback {

        private JsonObject payload;
        private double requestStartTime;

        public XhrResponseHandler() {
        }

        /**
         * Sets the payload which was sent to the server
         *
         * @param payload
         *            the payload which was sent to the server
         */
        public void setPayload(JsonObject payload) {
            this.payload = payload;
        }

        @Override
        public void onError(Request request, Throwable exception) {
            getConnectionStateHandler().xhrException(
                    new XhrConnectionError(request, payload, exception));
        }

        @Override
        public void onResponseReceived(Request request, Response response) {
            int statusCode = response.getStatusCode();

            if (statusCode != 200) {
                // There was a problem
                XhrConnectionError problemEvent = new XhrConnectionError(
                        request, payload, response);

                getConnectionStateHandler().xhrInvalidStatusCode(problemEvent);
                return;
            }

            getLogger().info("Server visit took " + Util.round(
                    Profiler.getRelativeTimeMillis() - requestStartTime, 3)
                    + "ms");

            // for(;;);["+ realJson +"]"
            String responseText = response.getText();

            ValueMap json = MessageHandler.parseWrappedJson(responseText);
            if (json == null) {
                // Invalid string (not wrapped as expected or can't parse)
                getConnectionStateHandler().xhrInvalidContent(
                        new XhrConnectionError(request, payload, response));
                return;
            }

            getConnectionStateHandler().xhrOk();
            getLogger().info("Received xhr message: " + responseText);
            getMessageHandler().handleMessage(json);
        }

        /**
         * Sets the relative time (see {@link Profiler#getRelativeTimeMillis()})
         * when the request was sent.
         *
         * @param requestStartTime
         *            the relative time when the request was sent
         */
        private void setRequestStartTime(double requestStartTime) {
            this.requestStartTime = requestStartTime;

        }
    };

    /**
     * Sends an asynchronous UIDL request to the server using the given URI.
     *
     * @param payload
     *            The URI to use for the request. May includes GET parameters
     * @throws RequestException
     *             if the request could not be sent
     */
    public void send(JsonObject payload) {
        RequestBuilder rb = new RequestBuilder(RequestBuilder.POST, getUri());
        // TODO enable timeout
        // rb.setTimeoutMillis(timeoutMillis);
        // TODO this should be configurable
        rb.setHeader("Content-Type", JsonConstants.JSON_CONTENT_TYPE);
        rb.setRequestData(payload.toJson());

        XhrResponseHandler responseHandler = createResponseHandler();
        responseHandler.setPayload(payload);
        responseHandler.setRequestStartTime(Profiler.getRelativeTimeMillis());

        rb.setCallback(responseHandler);

        getLogger().info("Sending xhr message to server: " + payload.toJson());
        try {
            final Request request = rb.send();

            if (webkitMaybeIgnoringRequests && BrowserInfo.get().isWebkit()) {
                final int retryTimeout = 250;
                new Timer() {
                    @Override
                    public void run() {
                        // Use native js to access private field in Request
                        if (resendRequest(request)
                                && webkitMaybeIgnoringRequests) {
                            // Schedule retry if still needed
                            schedule(retryTimeout);
                        }
                    }
                }.schedule(retryTimeout);
            }
        } catch (RequestException e) {
            getConnectionStateHandler()
                    .xhrException(new XhrConnectionError(null, payload, e));
        }
    }

    /**
     * Retrieves the URI to use when sending RPCs to the server
     *
     * @return The URI to use for server messages.
     */
    protected String getUri() {
        String uri = connection
                .translateVaadinUri(ApplicationConstants.APP_PROTOCOL_PREFIX
                        + ApplicationConstants.UIDL_PATH + '/');

        uri = SharedUtil.addGetParameters(uri, UIConstants.UI_ID_PARAMETER + "="
                + connection.getConfiguration().getUIId());

        return uri;

    }

    private ConnectionStateHandler getConnectionStateHandler() {
        return connection.getConnectionStateHandler();
    }

    private MessageHandler getMessageHandler() {
        return connection.getMessageHandler();
    }

    private static native boolean resendRequest(Request request)
    /*-{
        var xhr = [email protected]::xmlHttpRequest
        if (xhr == null) {
            // This might be called even though the request has completed,
            // if the webkitMaybeIgnoringRequests has been set to true on beforeunload
            // but unload was cancelled after that. It will then stay on until the following
            // request and if that request completes before we get here (<250mS), we will
            // hit this case.
            return false;
        }
        if (xhr.readyState != 1) {
            // Progressed to some other readyState -> no longer blocked
            return false;
        }
        try {
            xhr.send();
            return true;
        } catch (e) {
            // send throws exception if it is running for real
            return false;
        }
    }-*/;

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy