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

com.vaadin.client.communication.ServerRpcQueue 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.Collection;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.logging.Logger;

import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.vaadin.client.ApplicationConnection;
import com.vaadin.client.ConnectorMap;
import com.vaadin.client.metadata.Method;
import com.vaadin.client.metadata.NoDataException;
import com.vaadin.client.metadata.Type;
import com.vaadin.client.metadata.TypeDataStore;
import com.vaadin.shared.ApplicationConstants;
import com.vaadin.shared.communication.MethodInvocation;

import elemental.json.Json;
import elemental.json.JsonArray;
import elemental.json.JsonValue;

/**
 * Manages the queue of server invocations (RPC) which are waiting to be sent to
 * the server.
 *
 * @since 7.6
 * @author Vaadin Ltd
 */
public class ServerRpcQueue {

    /**
     * The pending method invocations that will be send to the server by
     * {@link #sendPendingCommand}. The key is defined differently based on
     * whether the method invocation is enqueued with lastonly. With lastonly
     * enabled, the method signature ( {@link MethodInvocation#getLastOnlyTag()}
     * ) is used as the key to make enable removing a previously enqueued
     * invocation. Without lastonly, an incremental id based on
     * {@link #lastInvocationTag} is used to get unique values.
     */
    private LinkedHashMap pendingInvocations = new LinkedHashMap<>();

    private int lastInvocationTag = 0;

    protected ApplicationConnection connection;
    private boolean flushPending = false;

    private boolean flushScheduled = false;

    public ServerRpcQueue() {

    }

    /**
     * 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;
    }

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

    /**
     * Removes any pending invocation of the given method from the queue
     *
     * @param invocation
     *            The invocation to remove
     */
    public void removeMatching(MethodInvocation invocation) {
        Iterator iter = pendingInvocations.values()
                .iterator();
        while (iter.hasNext()) {
            MethodInvocation mi = iter.next();
            if (mi.equals(invocation)) {
                iter.remove();
            }
        }
    }

    /**
     * Adds an explicit RPC method invocation to the send queue.
     *
     * @param invocation
     *            RPC method invocation
     * @param lastOnly
     *            true to remove all previously delayed invocations
     *            of the same method that were also enqueued with lastonly set
     *            to true. false to add invocation to
     *            the end of the queue without touching previously enqueued
     *            invocations.
     */
    public void add(MethodInvocation invocation, boolean lastOnly) {
        if (!connection.isApplicationRunning()) {
            getLogger().warning(
                    "Trying to invoke method on not yet started or stopped application");
            return;
        }
        String tag;
        if (lastOnly) {
            tag = invocation.getLastOnlyTag();
            assert !tag.matches(
                    "\\d+") : "getLastOnlyTag value must have at least one non-digit character";
            pendingInvocations.remove(tag);
        } else {
            tag = Integer.toString(lastInvocationTag++);
        }
        pendingInvocations.put(tag, invocation);
    }

    /**
     * Returns a collection of all queued method invocations
     * 

* The returned collection must not be modified in any way * * @return a collection of all queued method invocations */ public Collection getAll() { return pendingInvocations.values(); } /** * Clears the queue */ public void clear() { pendingInvocations.clear(); // Keep tag string short lastInvocationTag = 0; flushPending = false; } /** * Returns the current size of the queue * * @return the number of invocations in the queue */ public int size() { return pendingInvocations.size(); } /** * Returns the server RPC queue for the given application * * @param connection * the application connection which owns the queue * @return the server rpc queue for the given application */ public static ServerRpcQueue get(ApplicationConnection connection) { return connection.getServerRpcQueue(); } /** * Checks if the queue is empty * * @return true if the queue is empty, false otherwise */ public boolean isEmpty() { return size() == 0; } /** * Triggers a send of server RPC and legacy variable changes to the server. */ public void flush() { if (flushScheduled || isEmpty()) { return; } flushPending = true; flushScheduled = true; Scheduler.get().scheduleFinally(scheduledFlushCommand); } private final ScheduledCommand scheduledFlushCommand = new ScheduledCommand() { @Override public void execute() { flushScheduled = false; if (!isFlushPending()) { // Somebody else cleared the queue before we had the chance return; } connection.getMessageSender().sendInvocationsToServer(); } }; /** * Checks if a flush operation is pending * * @return true if a flush is pending, false otherwise */ public boolean isFlushPending() { return flushPending; } /** * Checks if a loading indicator should be shown when the RPCs have been * sent to the server and we are waiting for a response * * @return true if a loading indicator should be shown, false otherwise */ public boolean showLoadingIndicator() { for (MethodInvocation invocation : getAll()) { if (isLegacyVariableChange(invocation) || isJavascriptRpc(invocation)) { // Always show loading indicator for legacy requests return true; } else { Type type = new Type(invocation.getInterfaceName(), null); Method method = type.getMethod(invocation.getMethodName()); if (!TypeDataStore.isNoLoadingIndicator(method)) { return true; } } } return false; } /** * Returns the current invocations as JSON * * @return the current invocations in a JSON format ready to be sent to the * server */ public JsonArray toJson() { JsonArray json = Json.createArray(); if (isEmpty()) { return json; } for (MethodInvocation invocation : getAll()) { String connectorId = invocation.getConnectorId(); if (!connectorExists(connectorId)) { getLogger().info("Ignoring RPC for removed connector: " + connectorId + ": " + invocation.toString()); continue; } JsonArray invocationJson = Json.createArray(); invocationJson.set(0, connectorId); invocationJson.set(1, invocation.getInterfaceName()); invocationJson.set(2, invocation.getMethodName()); JsonArray paramJson = Json.createArray(); Type[] parameterTypes = null; if (!isLegacyVariableChange(invocation) && !isJavascriptRpc(invocation)) { try { Type type = new Type(invocation.getInterfaceName(), null); Method method = type.getMethod(invocation.getMethodName()); parameterTypes = method.getParameterTypes(); } catch (NoDataException e) { throw new RuntimeException( "No type data for " + invocation.toString(), e); } } for (int i = 0; i < invocation.getParameters().length; ++i) { // TODO non-static encoder? Type type = null; if (parameterTypes != null) { type = parameterTypes[i]; } Object value = invocation.getParameters()[i]; JsonValue jsonValue = JsonEncoder.encode(value, type, connection); paramJson.set(i, jsonValue); } invocationJson.set(3, paramJson); json.set(json.length(), invocationJson); } return json; } /** * Checks if the connector with the given id is still ok to use (has not * been removed) * * @param connectorId * the connector id to check * @return true if the connector exists, false otherwise */ private boolean connectorExists(String connectorId) { ConnectorMap connectorMap = ConnectorMap.get(connection); return connectorMap.hasConnector(connectorId) || connectorMap.isDragAndDropPaintable(connectorId); } /** * Checks if the given method invocation originates from Javascript * * @param invocation * the invocation to check * @return true if the method invocation originates from javascript, false * otherwise */ public static boolean isJavascriptRpc(MethodInvocation invocation) { return invocation instanceof JavaScriptMethodInvocation; } /** * Checks if the given method invocation represents a Vaadin 6 variable * change * * @param invocation * the invocation to check * @return true if the method invocation is a legacy variable change, false * otherwise */ public static boolean isLegacyVariableChange(MethodInvocation invocation) { return ApplicationConstants.UPDATE_VARIABLE_METHOD .equals(invocation.getInterfaceName()) && ApplicationConstants.UPDATE_VARIABLE_METHOD .equals(invocation.getMethodName()); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy