com.vaadin.client.ApplicationConnection Maven / Gradle / Ivy
/*
* Copyright 2000-2018 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;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Logger;
import com.google.gwt.aria.client.LiveValue;
import com.google.gwt.aria.client.RelevantValue;
import com.google.gwt.aria.client.Roles;
import com.google.gwt.core.client.Duration;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.dom.client.Element;
import com.google.gwt.event.shared.EventBus;
import com.google.gwt.event.shared.EventHandler;
import com.google.gwt.event.shared.GwtEvent;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.event.shared.HasHandlers;
import com.google.gwt.event.shared.SimpleEventBus;
import com.google.gwt.http.client.URL;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.ui.HasWidgets;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.client.ApplicationConfiguration.ErrorMessage;
import com.vaadin.client.communication.ConnectionStateHandler;
import com.vaadin.client.communication.Heartbeat;
import com.vaadin.client.communication.MessageHandler;
import com.vaadin.client.communication.MessageSender;
import com.vaadin.client.communication.RpcManager;
import com.vaadin.client.communication.ServerRpcQueue;
import com.vaadin.client.componentlocator.ComponentLocator;
import com.vaadin.client.metadata.ConnectorBundleLoader;
import com.vaadin.client.ui.AbstractComponentConnector;
import com.vaadin.client.ui.AbstractConnector;
import com.vaadin.client.ui.FontIcon;
import com.vaadin.client.ui.Icon;
import com.vaadin.client.ui.ImageIcon;
import com.vaadin.client.ui.VContextMenu;
import com.vaadin.client.ui.VNotification;
import com.vaadin.client.ui.VOverlay;
import com.vaadin.client.ui.ui.UIConnector;
import com.vaadin.shared.VaadinUriResolver;
import com.vaadin.shared.Version;
import com.vaadin.shared.communication.LegacyChangeVariablesInvocation;
import com.vaadin.shared.util.SharedUtil;
/**
* This is the client side communication "engine", managing client-server
* communication with its server side counterpart
* com.vaadin.server.VaadinService.
*
* Client-side connectors receive updates from the corresponding server-side
* connector (typically component) as state updates or RPC calls. The connector
* has the possibility to communicate back with its server side counter part
* through RPC calls.
*
* TODO document better
*
* Entry point classes (widgetsets) define onModuleLoad()
.
*/
public class ApplicationConnection implements HasHandlers {
@Deprecated
public static final String MODIFIED_CLASSNAME = StyleConstants.MODIFIED;
@Deprecated
public static final String DISABLED_CLASSNAME = StyleConstants.DISABLED;
@Deprecated
public static final String REQUIRED_CLASSNAME = StyleConstants.REQUIRED;
@Deprecated
public static final String REQUIRED_CLASSNAME_EXT = StyleConstants.REQUIRED_EXT;
@Deprecated
public static final String ERROR_CLASSNAME_EXT = StyleConstants.ERROR_EXT;
/**
* A string that, if found in a non-JSON response to a UIDL request, will
* cause the browser to refresh the page. If followed by a colon, optional
* whitespace, and a URI, causes the browser to synchronously load the URI.
*
*
* This allows, for instance, a servlet filter to redirect the application
* to a custom login page when the session expires. For example:
*
*
*
* if (sessionExpired) {
* response.setHeader("Content-Type", "text/html");
* response.getWriter().write(myLoginPageHtml + "<!-- Vaadin-Refresh: "
* + request.getContextPath() + " -->");
* }
*
*/
public static final String UIDL_REFRESH_TOKEN = "Vaadin-Refresh";
private final Map resourcesMap = new HashMap<>();
private WidgetSet widgetSet;
private VContextMenu contextMenu = null;
private final UIConnector uIConnector;
protected boolean cssLoaded = false;
/** Parameters for this application connection loaded from the web-page */
private ApplicationConfiguration configuration;
private final LayoutManager layoutManager;
private final RpcManager rpcManager;
/** Event bus for communication events */
private EventBus eventBus = GWT.create(SimpleEventBus.class);
public enum ApplicationState {
INITIALIZING, RUNNING, TERMINATED;
}
private ApplicationState applicationState = ApplicationState.INITIALIZING;
/**
* The communication handler methods are called at certain points during
* communication with the server. This allows for making add-ons that keep
* track of different aspects of the communication.
*/
public interface CommunicationHandler extends EventHandler {
void onRequestStarting(RequestStartingEvent e);
void onResponseHandlingStarted(ResponseHandlingStartedEvent e);
void onResponseHandlingEnded(ResponseHandlingEndedEvent e);
}
public static class RequestStartingEvent
extends ApplicationConnectionEvent {
public static Type TYPE = new Type<>();
public RequestStartingEvent(ApplicationConnection connection) {
super(connection);
}
@Override
public Type getAssociatedType() {
return TYPE;
}
@Override
protected void dispatch(CommunicationHandler handler) {
handler.onRequestStarting(this);
}
}
public static class ResponseHandlingEndedEvent
extends ApplicationConnectionEvent {
public static Type TYPE = new Type<>();
public ResponseHandlingEndedEvent(ApplicationConnection connection) {
super(connection);
}
@Override
public Type getAssociatedType() {
return TYPE;
}
@Override
protected void dispatch(CommunicationHandler handler) {
handler.onResponseHandlingEnded(this);
}
}
public abstract static class ApplicationConnectionEvent
extends GwtEvent {
private ApplicationConnection connection;
protected ApplicationConnectionEvent(ApplicationConnection connection) {
this.connection = connection;
}
public ApplicationConnection getConnection() {
return connection;
}
}
public static class ResponseHandlingStartedEvent
extends ApplicationConnectionEvent {
public ResponseHandlingStartedEvent(ApplicationConnection connection) {
super(connection);
}
public static Type TYPE = new Type<>();
@Override
public Type getAssociatedType() {
return TYPE;
}
@Override
protected void dispatch(CommunicationHandler handler) {
handler.onResponseHandlingStarted(this);
}
}
/**
* Event triggered when a application is stopped by calling
* {@link ApplicationConnection#setApplicationRunning(false)}.
*
* To listen for the event add a {@link ApplicationStoppedHandler} by
* invoking
* {@link ApplicationConnection#addHandler(GwtEvent.Type, ApplicationStoppedHandler)}
* to the {@link ApplicationConnection}
*
* @since 7.1.8
* @author Vaadin Ltd
*/
public static class ApplicationStoppedEvent
extends GwtEvent {
public static Type TYPE = new Type<>();
@Override
public Type getAssociatedType() {
return TYPE;
}
@Override
protected void dispatch(ApplicationStoppedHandler listener) {
listener.onApplicationStopped(this);
}
}
/**
* Allows custom handling of communication errors.
*/
public interface CommunicationErrorHandler {
/**
* Called when a communication error has occurred. Returning
* true
from this method suppresses error handling.
*
* @param details
* A string describing the error.
* @param statusCode
* The HTTP status code (e.g. 404, etc).
* @return true if the error reporting should be suppressed, false to
* perform normal error reporting.
*/
public boolean onError(String details, int statusCode);
}
/**
* A listener for listening to application stopped events. The listener can
* be added to a {@link ApplicationConnection} by invoking
* {@link ApplicationConnection#addHandler(GwtEvent.Type, ApplicationStoppedHandler)}
*
* @since 7.1.8
* @author Vaadin Ltd
*/
public interface ApplicationStoppedHandler extends EventHandler {
/**
* Triggered when the {@link ApplicationConnection} marks a previously
* running application as stopped by invoking
* {@link ApplicationConnection#setApplicationRunning(false)}.
*
* @param event
* the event triggered by the {@link ApplicationConnection}
*/
void onApplicationStopped(ApplicationStoppedEvent event);
}
private CommunicationErrorHandler communicationErrorDelegate = null;
private VLoadingIndicator loadingIndicator;
private Heartbeat heartbeat = GWT.create(Heartbeat.class);
private boolean tooltipInitialized = false;
private final VaadinUriResolver uriResolver = new VaadinUriResolver() {
@Override
protected String getVaadinDirUrl() {
return getConfiguration().getVaadinDirUrl();
}
@Override
protected String getServiceUrlParameterName() {
return getConfiguration().getServiceUrlParameterName();
}
@Override
protected String getServiceUrl() {
return getConfiguration().getServiceUrl();
}
@Override
protected String getThemeUri() {
return ApplicationConnection.this.getThemeUri();
}
@Override
protected String encodeQueryStringParameterValue(String queryString) {
return URL.encodeQueryString(queryString);
}
@Override
protected String getContextRootUrl() {
return getConfiguration().getContextRootUrl();
}
@Override
protected String getFrontendUrl() {
String url = getConfiguration().getFrontendUrl();
assert url.endsWith("/");
return url;
}
};
public static class MultiStepDuration extends Duration {
private int previousStep = elapsedMillis();
public void logDuration(String message) {
logDuration(message, 0);
}
public void logDuration(String message, int minDuration) {
int currentTime = elapsedMillis();
int stepDuration = currentTime - previousStep;
if (stepDuration >= minDuration) {
getLogger().info(message + ": " + stepDuration + " ms");
}
previousStep = currentTime;
}
}
public ApplicationConnection() {
// Assuming UI data is eagerly loaded
ConnectorBundleLoader.get()
.loadBundle(ConnectorBundleLoader.EAGER_BUNDLE_NAME, null);
uIConnector = GWT.create(UIConnector.class);
rpcManager = GWT.create(RpcManager.class);
layoutManager = GWT.create(LayoutManager.class);
tooltip = GWT.create(VTooltip.class);
loadingIndicator = GWT.create(VLoadingIndicator.class);
serverRpcQueue = GWT.create(ServerRpcQueue.class);
connectionStateHandler = GWT.create(ConnectionStateHandler.class);
messageHandler = GWT.create(MessageHandler.class);
messageSender = GWT.create(MessageSender.class);
}
public void init(WidgetSet widgetSet, ApplicationConfiguration cnf) {
getLogger().info("Starting application " + cnf.getRootPanelId());
getLogger().info("Using theme: " + cnf.getThemeName());
getLogger().info("Vaadin application servlet version: "
+ cnf.getServletVersion());
if (!cnf.getServletVersion().equals(Version.getFullVersion())) {
getLogger().severe(
"Warning: your widget set seems to be built with a different "
+ "version than the one used on server. Unexpected "
+ "behavior may occur.");
}
this.widgetSet = widgetSet;
configuration = cnf;
layoutManager.setConnection(this);
loadingIndicator.setConnection(this);
serverRpcQueue.setConnection(this);
messageHandler.setConnection(this);
messageSender.setConnection(this);
dependencyLoader.setConnection(this);
ComponentLocator componentLocator = new ComponentLocator(this);
String appRootPanelName = cnf.getRootPanelId();
// remove the end (window name) of autogenerated rootpanel id
appRootPanelName = appRootPanelName.replaceFirst("-\\d+$", "");
initializeTestbenchHooks(componentLocator, appRootPanelName);
initializeClientHooks();
if (cnf.getRootElement() != null) {
uIConnector.init(cnf.getRootElement(), this);
} else {
uIConnector.init(cnf.getRootPanelId(), this);
}
// Connection state handler preloads the reconnect dialog, which uses
// overlay container. This in turn depends on VUI being attached
// (done in uiConnector.init)
connectionStateHandler.setConnection(this);
tooltip.setOwner(uIConnector.getWidget());
getLoadingIndicator().show();
heartbeat.init(this);
// Ensure the overlay container is added to the dom and set as a live
// area for assistive devices
Element overlayContainer = VOverlay.getOverlayContainer(this);
Roles.getAlertRole().setAriaLiveProperty(overlayContainer,
LiveValue.ASSERTIVE);
VOverlay.setOverlayContainerLabel(this,
getUIConnector().getState().overlayContainerLabel);
Roles.getAlertRole().setAriaRelevantProperty(overlayContainer,
RelevantValue.ADDITIONS);
}
/**
* Starts this application. Don't call this method directly - it's called by
* {@link ApplicationConfiguration#startNextApplication()}, which should be
* called once this application has started (first response received) or
* failed to start. This ensures that the applications are started in order,
* to avoid session-id problems.
*
*/
public void start() {
String jsonText = configuration.getUIDL();
if (jsonText == null) {
// initial UIDL not in DOM, request from server
getMessageSender().resynchronize();
} else {
// initial UIDL provided in DOM, continue as if returned by request
// Hack to avoid logging an error in endRequest()
getMessageSender().startRequest();
getMessageHandler()
.handleMessage(MessageHandler.parseJson(jsonText));
}
// Tooltip can't be created earlier because the
// necessary fields are not setup to add it in the
// correct place in the DOM
if (!tooltipInitialized) {
tooltipInitialized = true;
ApplicationConfiguration.runWhenDependenciesLoaded(
() -> getVTooltip().initializeAssistiveTooltips());
}
}
/**
* Checks if there is some work to be done on the client side
*
* @return true if the client has some work to be done, false otherwise
*/
private boolean isActive() {
return !getMessageHandler().isInitialUidlHandled() || isWorkPending()
|| getMessageSender().hasActiveRequest()
|| isExecutingDeferredCommands();
}
private native void initializeTestbenchHooks(
ComponentLocator componentLocator, String ttAppId)
/*-{
var ap = this;
var client = {};
client.isActive = $entry(function() {
return [email protected]::isActive()();
});
var vi = [email protected]::getVersionInfo()();
if (vi) {
client.getVersionInfo = function() {
return vi;
}
}
client.getProfilingData = $entry(function() {
var smh = [email protected]::getMessageHandler()();
var pd = [
[email protected]::lastProcessingTime,
[email protected]::totalProcessingTime
];
if (null != [email protected]::serverTimingInfo) {
pd = pd.concat([email protected]::serverTimingInfo);
} else {
pd = pd.concat(-1, -1);
}
pd[pd.length] = [email protected]::bootstrapTime;
return pd;
});
client.getElementByPath = $entry(function(id) {
return componentLocator.@com.vaadin.client.componentlocator.ComponentLocator::getElementByPath(Ljava/lang/String;)(id);
});
client.getElementByPathStartingAt = $entry(function(id, element) {
return componentLocator.@com.vaadin.client.componentlocator.ComponentLocator::getElementByPathStartingAt(Ljava/lang/String;Lcom/google/gwt/dom/client/Element;)(id, element);
});
client.getElementsByPath = $entry(function(id) {
return componentLocator.@com.vaadin.client.componentlocator.ComponentLocator::getElementsByPath(Ljava/lang/String;)(id);
});
client.getElementsByPathStartingAt = $entry(function(id, element) {
return componentLocator.@com.vaadin.client.componentlocator.ComponentLocator::getElementsByPathStartingAt(Ljava/lang/String;Lcom/google/gwt/dom/client/Element;)(id, element);
});
client.getPathForElement = $entry(function(element) {
return componentLocator.@com.vaadin.client.componentlocator.ComponentLocator::getPathForElement(Lcom/google/gwt/dom/client/Element;)(element);
});
client.initializing = false;
$wnd.vaadin.clients[ttAppId] = client;
}-*/;
/**
* Helper for tt initialization
*/
private JavaScriptObject getVersionInfo() {
return configuration.getVersionInfoJSObject();
}
/**
* Publishes a JavaScript API for mash-up applications.
*
* vaadin.forceSync()
sends pending variable changes, in
* effect synchronizing the server and client state. This is done for all
* applications on host page.
* vaadin.postRequestHooks
is a map of functions which gets
* called after each XHR made by vaadin application. Note, that it is
* attaching js functions responsibility to create the variable like this:
*
*
* if (!vaadin.postRequestHooks) {vaadin.postRequestHooks = new Object();}
* postRequestHooks.myHook = function(appId) {
* if (appId == "MyAppOfInterest") {
* // do the staff you need on xhr activity
* }
* }
*
First parameter passed to these functions is the identifier
* of Vaadin application that made the request.
*
*
* TODO make this multi-app aware
*/
private native void initializeClientHooks()
/*-{
var app = this;
var oldSync;
if ($wnd.vaadin.forceSync) {
oldSync = $wnd.vaadin.forceSync;
}
$wnd.vaadin.forceSync = $entry(function() {
if (oldSync) {
oldSync();
}
var sender = [email protected]::messageSender;
[email protected]::resynchronize()();
});
var oldForceLayout;
if ($wnd.vaadin.forceLayout) {
oldForceLayout = $wnd.vaadin.forceLayout;
}
$wnd.vaadin.forceLayout = $entry(function() {
if (oldForceLayout) {
oldForceLayout();
}
[email protected]::forceLayout()();
});
}-*/;
/**
* Requests an analyze of layouts, to find inconsistencies. Exclusively used
* for debugging during development.
*
* @deprecated as of 7.1. Replaced by {@link UIConnector#analyzeLayouts()}
*/
@Deprecated
public void analyzeLayouts() {
getUIConnector().analyzeLayouts();
}
/**
* Sends a request to the server to print details to console that will help
* the developer to locate the corresponding server-side connector in the
* source code.
*
* @param serverConnector
* @deprecated as of 7.1. Replaced by
* {@link UIConnector#showServerDebugInfo(ServerConnector)}
*/
@Deprecated
void highlightConnector(ServerConnector serverConnector) {
getUIConnector().showServerDebugInfo(serverConnector);
}
int cssWaits = 0;
protected ServerRpcQueue serverRpcQueue;
protected ConnectionStateHandler connectionStateHandler;
protected MessageHandler messageHandler;
protected MessageSender messageSender;
static final int MAX_CSS_WAITS = 100;
public void executeWhenCSSLoaded(final Command c) {
if (!isCSSLoaded() && cssWaits < MAX_CSS_WAITS) {
(new Timer() {
@Override
public void run() {
executeWhenCSSLoaded(c);
}
}).schedule(50);
// Show this message just once
if (cssWaits++ == 0) {
getLogger().warning("Assuming CSS loading is not complete, "
+ "postponing render phase. "
+ "(.v-loading-indicator height == 0)");
}
} else {
cssLoaded = true;
if (cssWaits >= MAX_CSS_WAITS) {
getLogger().severe("CSS files may have not loaded properly.");
}
c.execute();
}
}
/**
* Checks whether or not the CSS is loaded. By default checks the size of
* the loading indicator element.
*
* @return
*/
protected boolean isCSSLoaded() {
return cssLoaded
|| getLoadingIndicator().getElement().getOffsetHeight() != 0;
}
/**
* Shows the communication error notification.
*
* @param details
* Optional details.
* @param statusCode
* The status code returned for the request
*
*/
public void showCommunicationError(String details, int statusCode) {
getLogger().severe("Communication error: " + details);
showError(details, configuration.getCommunicationError());
}
/**
* Shows the authentication error notification.
*
* @param details
* Optional details.
*/
public void showAuthenticationError(String details) {
getLogger().severe("Authentication error: " + details);
showError(details, configuration.getAuthorizationError());
}
/**
* Shows the session expiration notification.
*
* @param details
* Optional details.
*/
public void showSessionExpiredError(String details) {
getLogger().severe("Session expired: " + details);
showError(details, configuration.getSessionExpiredError());
}
/**
* Shows an error notification.
*
* @param details
* Optional details.
* @param message
* An ErrorMessage describing the error.
*/
protected void showError(String details, ErrorMessage message) {
VNotification.showError(this, message.getCaption(),
message.getMessage(), details, message.getUrl());
}
/**
* Checks if the client has running or scheduled commands
*/
private boolean isWorkPending() {
ConnectorMap connectorMap = getConnectorMap();
JsArrayObject connectors = connectorMap
.getConnectorsAsJsArray();
int size = connectors.size();
for (int i = 0; i < size; i++) {
ServerConnector conn = connectors.get(i);
if (isWorkPending(conn)) {
return true;
}
if (conn instanceof ComponentConnector) {
ComponentConnector compConn = (ComponentConnector) conn;
if (isWorkPending(compConn.getWidget())) {
return true;
}
}
}
return false;
}
private static boolean isWorkPending(Object object) {
return object instanceof DeferredWorker
&& ((DeferredWorker) object).isWorkPending();
}
/**
* Checks if deferred commands are (potentially) still being executed as a
* result of an update from the server. Returns true if a deferred command
* might still be executing, false otherwise. This will not work correctly
* if a deferred command is added in another deferred command.
*
* Used by the native "client.isActive" function.
*
*
* @return true if deferred commands are (potentially) being executed, false
* otherwise
*/
private boolean isExecutingDeferredCommands() {
Scheduler s = Scheduler.get();
if (s instanceof VSchedulerImpl) {
return ((VSchedulerImpl) s).hasWorkQueued();
} else {
return false;
}
}
/**
* Returns the loading indicator used by this ApplicationConnection.
*
* @return The loading indicator for this ApplicationConnection
*/
public VLoadingIndicator getLoadingIndicator() {
return loadingIndicator;
}
/**
* Determines whether or not the loading indicator is showing.
*
* @return true if the loading indicator is visible
* @deprecated As of 7.1. Use {@link #getLoadingIndicator()} and
* {@link VLoadingIndicator#isVisible()}.isVisible() instead.
*/
@Deprecated
public boolean isLoadingIndicatorVisible() {
return getLoadingIndicator().isVisible();
}
private void addVariableToQueue(String connectorId, String variableName,
Object value, boolean immediate) {
boolean lastOnly = !immediate;
// note that type is now deduced from value
serverRpcQueue.add(new LegacyChangeVariablesInvocation(connectorId,
variableName, value), lastOnly);
if (immediate) {
serverRpcQueue.flush();
}
}
/**
* @deprecated as of 7.6, use {@link ServerRpcQueue#flush()}
*/
@Deprecated
public void sendPendingVariableChanges() {
serverRpcQueue.flush();
}
/**
* Sends a new value for the given paintables given variable to the server.
*
* The update is actually queued to be sent at a suitable time. If immediate
* is true, the update is sent as soon as possible. If immediate is false,
* the update will be sent along with the next immediate update.
*
*
* @param paintableId
* the id of the paintable that owns the variable
* @param variableName
* the name of the variable
* @param newValue
* the new value to be sent
* @param immediate
* true if the update is to be sent as soon as possible
*/
public void updateVariable(String paintableId, String variableName,
ServerConnector newValue, boolean immediate) {
addVariableToQueue(paintableId, variableName, newValue, immediate);
}
/**
* Sends a new value for the given paintables given variable to the server.
*
* The update is actually queued to be sent at a suitable time. If immediate
* is true, the update is sent as soon as possible. If immediate is false,
* the update will be sent along with the next immediate update.
*
*
* @param paintableId
* the id of the paintable that owns the variable
* @param variableName
* the name of the variable
* @param newValue
* the new value to be sent
* @param immediate
* true if the update is to be sent as soon as possible
*/
public void updateVariable(String paintableId, String variableName,
String newValue, boolean immediate) {
addVariableToQueue(paintableId, variableName, newValue, immediate);
}
/**
* Sends a new value for the given paintables given variable to the server.
*
* The update is actually queued to be sent at a suitable time. If immediate
* is true, the update is sent as soon as possible. If immediate is false,
* the update will be sent along with the next immediate update.
*
*
* @param paintableId
* the id of the paintable that owns the variable
* @param variableName
* the name of the variable
* @param newValue
* the new value to be sent
* @param immediate
* true if the update is to be sent as soon as possible
*/
public void updateVariable(String paintableId, String variableName,
int newValue, boolean immediate) {
addVariableToQueue(paintableId, variableName, newValue, immediate);
}
/**
* Sends a new value for the given paintables given variable to the server.
*
* The update is actually queued to be sent at a suitable time. If immediate
* is true, the update is sent as soon as possible. If immediate is false,
* the update will be sent along with the next immediate update.
*
*
* @param paintableId
* the id of the paintable that owns the variable
* @param variableName
* the name of the variable
* @param newValue
* the new value to be sent
* @param immediate
* true if the update is to be sent as soon as possible
*/
public void updateVariable(String paintableId, String variableName,
long newValue, boolean immediate) {
addVariableToQueue(paintableId, variableName, newValue, immediate);
}
/**
* Sends a new value for the given paintables given variable to the server.
*
* The update is actually queued to be sent at a suitable time. If immediate
* is true, the update is sent as soon as possible. If immediate is false,
* the update will be sent along with the next immediate update.
*
*
* @param paintableId
* the id of the paintable that owns the variable
* @param variableName
* the name of the variable
* @param newValue
* the new value to be sent
* @param immediate
* true if the update is to be sent as soon as possible
*/
public void updateVariable(String paintableId, String variableName,
float newValue, boolean immediate) {
addVariableToQueue(paintableId, variableName, newValue, immediate);
}
/**
* Sends a new value for the given paintables given variable to the server.
*
* The update is actually queued to be sent at a suitable time. If immediate
* is true, the update is sent as soon as possible. If immediate is false,
* the update will be sent along with the next immediate update.
*
*
* @param paintableId
* the id of the paintable that owns the variable
* @param variableName
* the name of the variable
* @param newValue
* the new value to be sent
* @param immediate
* true if the update is to be sent as soon as possible
*/
public void updateVariable(String paintableId, String variableName,
double newValue, boolean immediate) {
addVariableToQueue(paintableId, variableName, newValue, immediate);
}
/**
* Sends a new value for the given paintables given variable to the server.
*
* The update is actually queued to be sent at a suitable time. If immediate
* is true, the update is sent as soon as possible. If immediate is false,
* the update will be sent along with the next immediate update.
*
*
* @param paintableId
* the id of the paintable that owns the variable
* @param variableName
* the name of the variable
* @param newValue
* the new value to be sent
* @param immediate
* true if the update is to be sent as soon as possible
*/
public void updateVariable(String paintableId, String variableName,
boolean newValue, boolean immediate) {
addVariableToQueue(paintableId, variableName, newValue, immediate);
}
/**
* Sends a new value for the given paintables given variable to the server.
*
* The update is actually queued to be sent at a suitable time. If immediate
* is true, the update is sent as soon as possible. If immediate is false,
* the update will be sent along with the next immediate update.
*
*
* @param paintableId
* the id of the paintable that owns the variable
* @param variableName
* the name of the variable
* @param map
* the new values to be sent
* @param immediate
* true if the update is to be sent as soon as possible
*/
public void updateVariable(String paintableId, String variableName,
Map map, boolean immediate) {
addVariableToQueue(paintableId, variableName, map, immediate);
}
/**
* Sends a new value for the given paintables given variable to the server.
*
* The update is actually queued to be sent at a suitable time. If immediate
* is true, the update is sent as soon as possible. If immediate is false,
* the update will be sent along with the next immediate update.
*
* A null array is sent as an empty array.
*
* @param paintableId
* the id of the paintable that owns the variable
* @param variableName
* the name of the variable
* @param values
* the new value to be sent
* @param immediate
* true if the update is to be sent as soon as possible
*/
public void updateVariable(String paintableId, String variableName,
String[] values, boolean immediate) {
addVariableToQueue(paintableId, variableName, values, immediate);
}
/**
* Sends a new value for the given paintables given variable to the server.
*
* The update is actually queued to be sent at a suitable time. If immediate
* is true, the update is sent as soon as possible. If immediate is false,
* the update will be sent along with the next immediate update.
*
* A null array is sent as an empty array.
*
* @param paintableId
* the id of the paintable that owns the variable
* @param variableName
* the name of the variable
* @param values
* the new value to be sent
* @param immediate
* true if the update is to be sent as soon as possible
*/
public void updateVariable(String paintableId, String variableName,
Object[] values, boolean immediate) {
addVariableToQueue(paintableId, variableName, values, immediate);
}
/**
* Does absolutely nothing. Replaced by {@link LayoutManager}.
*
* @param container
* @deprecated As of 7.0, serves no purpose
*/
@Deprecated
public void runDescendentsLayout(HasWidgets container) {
}
/**
* This will cause re-layouting of all components. Mainly used for
* development. Published to JavaScript.
*/
public void forceLayout() {
Duration duration = new Duration();
layoutManager.forceLayout();
getLogger().info("forceLayout in " + duration.elapsedMillis() + " ms");
}
/**
* Returns false
*
* @param paintable
* @return false, always
* @deprecated As of 7.0, serves no purpose
*/
@Deprecated
private boolean handleComponentRelativeSize(ComponentConnector paintable) {
return false;
}
/**
* Returns false.
*
* @param paintable
* @return false, always
* @deprecated As of 7.0, serves no purpose
*/
@Deprecated
public boolean handleComponentRelativeSize(Widget widget) {
return handleComponentRelativeSize(connectorMap.getConnector(widget));
}
@Deprecated
public ComponentConnector getPaintable(UIDL uidl) {
// Non-component connectors shouldn't be painted from legacy connectors
return (ComponentConnector) getConnector(uidl.getId(),
Integer.parseInt(uidl.getTag()));
}
/**
* Get either an existing ComponentConnector or create a new
* ComponentConnector with the given type and id.
*
* If a ComponentConnector with the given id already exists, returns it.
* Otherwise creates and registers a new ComponentConnector of the given
* type.
*
* @param connectorId
* Id of the paintable
* @param connectorType
* Type of the connector, as passed from the server side
*
* @return Either an existing ComponentConnector or a new ComponentConnector
* of the given type
*/
public ServerConnector getConnector(String connectorId, int connectorType) {
if (!connectorMap.hasConnector(connectorId)) {
return createAndRegisterConnector(connectorId, connectorType);
}
return connectorMap.getConnector(connectorId);
}
/**
* Creates a new ServerConnector with the given type and id.
*
* Creates and registers a new ServerConnector of the given type. Should
* never be called with the connector id of an existing connector.
*
* @param connectorId
* Id of the new connector
* @param connectorType
* Type of the connector, as passed from the server side
*
* @return A new ServerConnector of the given type
*/
private ServerConnector createAndRegisterConnector(String connectorId,
int connectorType) {
Profiler.enter("ApplicationConnection.createAndRegisterConnector");
// Create and register a new connector with the given type
ServerConnector p = widgetSet.createConnector(connectorType,
configuration);
connectorMap.registerConnector(connectorId, p);
p.doInit(connectorId, this);
Profiler.leave("ApplicationConnection.createAndRegisterConnector");
return p;
}
/**
* Gets a resource that has been pre-loaded via UIDL, such as custom
* layouts.
*
* @param name
* identifier of the resource to get
* @return the resource
*/
public String getResource(String name) {
return resourcesMap.get(name);
}
/**
* Sets a resource that has been pre-loaded via UIDL, such as custom
* layouts.
*
* @since 7.6
* @param name
* identifier of the resource to Set
* @param resource
* the resource
*/
public void setResource(String name, String resource) {
resourcesMap.put(name, resource);
}
/**
* Singleton method to get instance of app's context menu.
*
* @return VContextMenu object
*/
public VContextMenu getContextMenu() {
if (contextMenu == null) {
contextMenu = new VContextMenu();
contextMenu.setOwner(uIConnector.getWidget());
DOM.setElementProperty(contextMenu.getElement(), "id",
"PID_VAADIN_CM");
}
return contextMenu;
}
/**
* Gets an {@link Icon} instance corresponding to a URI.
*
* @since 7.2
* @param uri
* @return Icon object
*/
public Icon getIcon(String uri) {
Icon icon;
if (uri == null) {
return null;
} else if (FontIcon.isFontIconUri(uri)) {
icon = GWT.create(FontIcon.class);
} else {
icon = GWT.create(ImageIcon.class);
}
icon.setUri(translateVaadinUri(uri));
return icon;
}
/**
* Translates custom protocols in UIDL URI's to be recognizable by browser.
* All uri's from UIDL should be routed via this method before giving them
* to browser due URI's in UIDL may contain custom protocols like theme://.
*
* @param uidlUri
* Vaadin URI from uidl
* @return translated URI ready for browser
*/
public String translateVaadinUri(String uidlUri) {
return uriResolver.resolveVaadinUri(uidlUri);
}
/**
* Gets the URI for the current theme. Can be used to reference theme
* resources.
*
* @return URI to the current theme
*/
public String getThemeUri() {
return configuration.getVaadinDirUrl() + "themes/"
+ getUIConnector().getActiveTheme();
}
/* Extended title handling */
private final VTooltip tooltip;
private ConnectorMap connectorMap = GWT.create(ConnectorMap.class);
private final DependencyLoader dependencyLoader = GWT
.create(DependencyLoader.class);
/**
* Use to notify that the given component's caption has changed; layouts may
* have to be recalculated.
*
* @param widget
* The Widget whose caption has changed
* @deprecated As of 7.0.2, has not had any effect for a long time
*/
@Deprecated
public void captionSizeUpdated(Widget widget) {
// This doesn't do anything, it's just kept here for compatibility
}
/**
* Gets the main view.
*
* @return the main view
*/
public UIConnector getUIConnector() {
return uIConnector;
}
/**
* Gets the {@link ApplicationConfiguration} for the current application.
*
* @see ApplicationConfiguration
* @return the configuration for this application
*/
public ApplicationConfiguration getConfiguration() {
return configuration;
}
/**
* Checks if there is a registered server side listener for the event. The
* list of events which has server side listeners is updated automatically
* before the component is updated so the value is correct if called from
* updatedFromUIDL.
*
* @param connector
* The connector to register event listeners for
* @param eventIdentifier
* The identifier for the event
* @return true if at least one listener has been registered on server side
* for the event identified by eventIdentifier.
* @deprecated As of 7.0. Use
* {@link AbstractConnector#hasEventListener(String)} instead
*/
@Deprecated
public boolean hasEventListeners(ComponentConnector connector,
String eventIdentifier) {
return connector.hasEventListener(eventIdentifier);
}
/**
* Adds the get parameters to the uri and returns the new uri that contains
* the parameters.
*
* @param uri
* The uri to which the parameters should be added.
* @param extraParams
* One or more parameters in the format "a=b" or "c=d&e=f". An
* empty string is allowed but will not modify the url.
* @return The modified URI with the get parameters in extraParams added.
* @deprecated Use {@link SharedUtil#addGetParameters(String,String)}
* instead
*/
@Deprecated
public static String addGetParameters(String uri, String extraParams) {
return SharedUtil.addGetParameters(uri, extraParams);
}
ConnectorMap getConnectorMap() {
return connectorMap;
}
/**
* @deprecated As of 7.0. No longer serves any purpose.
*/
@Deprecated
public void unregisterPaintable(ServerConnector p) {
getLogger().info("unregisterPaintable (unnecessarily) called for "
+ Util.getConnectorString(p));
}
/**
* Get VTooltip instance related to application connection.
*
* @return VTooltip instance
*/
public VTooltip getVTooltip() {
return tooltip;
}
/**
* Method provided for backwards compatibility. Duties previously done by
* this method is now handled by the state change event handler in
* AbstractComponentConnector. The only function this method has is to
* return true if the UIDL is a "cached" update.
*
* @param component
* @param uidl
* @param manageCaption
* @deprecated As of 7.0, no longer serves any purpose
* @return
*/
@Deprecated
public boolean updateComponent(Widget component, UIDL uidl,
boolean manageCaption) {
ComponentConnector connector = getConnectorMap()
.getConnector(component);
if (!AbstractComponentConnector.isRealUpdate(uidl)) {
return true;
}
if (!manageCaption) {
getLogger().warning(Util.getConnectorString(connector)
+ " called updateComponent with manageCaption=false. The parameter was ignored - override delegateCaption() to return false instead. It is however not recommended to use caption this way at all.");
}
return false;
}
/**
* @deprecated As of 7.0. Use
* {@link AbstractComponentConnector#hasEventListener(String)}
* instead
*/
@Deprecated
public boolean hasEventListeners(Widget widget, String eventIdentifier) {
ComponentConnector connector = getConnectorMap().getConnector(widget);
if (connector == null) {
/*
* No connector will exist in cases where Vaadin widgets have been
* re-used without implementing server<->client communication.
*/
return false;
}
return hasEventListeners(connector, eventIdentifier);
}
LayoutManager getLayoutManager() {
return layoutManager;
}
/**
* Schedules a heartbeat request to occur after the configured heartbeat
* interval elapses if the interval is a positive number. Otherwise, does
* nothing.
*
* @deprecated as of 7.2, use {@link Heartbeat#schedule()} instead
*/
@Deprecated
protected void scheduleHeartbeat() {
heartbeat.schedule();
}
/**
* Sends a heartbeat request to the server.
*
* Heartbeat requests are used to inform the server that the client-side is
* still alive. If the client page is closed or the connection lost, the
* server will eventually close the inactive UI.
*
* @deprecated as of 7.2, use {@link Heartbeat#send()} instead
*/
@Deprecated
protected void sendHeartbeat() {
heartbeat.send();
}
public void handleCommunicationError(String details, int statusCode) {
boolean handled = false;
if (communicationErrorDelegate != null) {
handled = communicationErrorDelegate.onError(details, statusCode);
}
if (!handled) {
showCommunicationError(details, statusCode);
}
}
/**
* Sets the delegate that is called whenever a communication error occurrs.
*
* @param delegate
* the delegate.
*/
public void setCommunicationErrorDelegate(
CommunicationErrorHandler delegate) {
communicationErrorDelegate = delegate;
}
public void setApplicationRunning(boolean applicationRunning) {
if (getApplicationState() == ApplicationState.TERMINATED) {
if (applicationRunning) {
getLogger().severe(
"Tried to restart a terminated application. This is not supported");
} else {
getLogger().warning(
"Tried to stop a terminated application. This should not be done");
}
return;
} else if (getApplicationState() == ApplicationState.INITIALIZING) {
if (applicationRunning) {
applicationState = ApplicationState.RUNNING;
} else {
getLogger().warning(
"Tried to stop the application before it has started. This should not be done");
}
} else if (getApplicationState() == ApplicationState.RUNNING) {
if (!applicationRunning) {
applicationState = ApplicationState.TERMINATED;
eventBus.fireEvent(new ApplicationStoppedEvent());
} else {
getLogger().warning(
"Tried to start an already running application. This should not be done");
}
}
}
/**
* Checks if the application is in the {@link ApplicationState#RUNNING}
* state.
*
* @since 7.6
* @return true if the application is in the running state, false otherwise
*/
public boolean isApplicationRunning() {
return applicationState == ApplicationState.RUNNING;
}
public HandlerRegistration addHandler(
GwtEvent.Type type, H handler) {
return eventBus.addHandler(type, handler);
}
@Override
public void fireEvent(GwtEvent event) {
eventBus.fireEvent(event);
}
/**
* Calls {@link ComponentConnector#flush()} on the active connector. Does
* nothing if there is no active (focused) connector.
*/
public void flushActiveConnector() {
ComponentConnector activeConnector = getActiveConnector();
if (activeConnector == null) {
return;
}
activeConnector.flush();
}
/**
* Gets the active connector for the focused element in the browser.
*
* @return the connector for the focused element or null
if
* none found or no element is focused.
*/
private ComponentConnector getActiveConnector() {
Element focusedElement = WidgetUtil.getFocusedElement();
if (focusedElement == null) {
return null;
}
return Util.getConnectorForElement(this, RootPanel.get(),
focusedElement);
}
private static Logger getLogger() {
return Logger.getLogger(ApplicationConnection.class.getName());
}
/**
* Returns the hearbeat instance.
*/
public Heartbeat getHeartbeat() {
return heartbeat;
}
/**
* Returns the state of this application. An application state goes from
* "initializing" to "running" to "stopped". There is no way for an
* application to go back to a previous state, i.e. a stopped application
* can never be re-started
*
* @since 7.6
* @return the current state of this application
*/
public ApplicationState getApplicationState() {
return applicationState;
}
/**
* Gets the server RPC queue for this application.
*
* @since 7.6
* @return the server RPC queue
*/
public ServerRpcQueue getServerRpcQueue() {
return serverRpcQueue;
}
/**
* Gets the communication error handler for this application.
*
* @since 7.6
* @return the server RPC queue
*/
public ConnectionStateHandler getConnectionStateHandler() {
return connectionStateHandler;
}
/**
* Gets the (server to client) message handler for this application.
*
* @since 7.6
* @return the message handler
*/
public MessageHandler getMessageHandler() {
return messageHandler;
}
/**
* Gets the server rpc manager for this application.
*
* @since 7.6
* @return the server rpc manager
*/
public RpcManager getRpcManager() {
return rpcManager;
}
/**
* Gets the (client to server) message sender for this application.
*
* @since 7.6
* @return the message sender
*/
public MessageSender getMessageSender() {
return messageSender;
}
/**
* @since 7.6
* @return the widget set
*/
public WidgetSet getWidgetSet() {
return widgetSet;
}
public int getLastSeenServerSyncId() {
return getMessageHandler().getLastSeenServerSyncId();
}
/**
* Gets the instance which handles loading of dependencies.
*
* @return the dependency loader for this connection
*/
public DependencyLoader getDependencyLoader() {
return dependencyLoader;
}
}