Browse Source

Initial version of the reconnect dialog and a problem handler which uses it (#11733)

Limitations
* Does not take Push into account
* Hard coded parameters
* Only theme for Valo

Change-Id: Iddb12d20391bcd30dc7289b7ea694ac3fbbd116d
tags/7.6.0.alpha5
Artur Signell 9 years ago
parent
commit
33751bb59b

+ 2
- 0
WebContent/VAADIN/themes/valo/shared/_global.scss View File

@@ -2,6 +2,7 @@
@import "contextmenu";
@import "overlay";
@import "tooltip";
@import "reconnect-dialog";


/*
@@ -374,6 +375,7 @@ $valo-shared-pathPrefix: null;

@include valo-contextmenu;

@include valo-reconnect-dialog;
}



+ 18
- 0
WebContent/VAADIN/themes/valo/shared/_reconnect-dialog.scss View File

@@ -0,0 +1,18 @@
@mixin valo-reconnect-dialog {
.v-reconnect-dialog {
color: white;
@include valo-notification-bar-style;
@include valo-notification-system-style;
text-align: center;
.spinner {
@include valo-spinner;
display: inline-block;
margin-top: 10px;
visibility: hidden;
}

&.active .spinner {
visibility: visible;
}
}
}

+ 2
- 2
client/src/com/vaadin/client/ApplicationConnection.java View File

@@ -46,8 +46,8 @@ import com.vaadin.client.ApplicationConnection.ApplicationStoppedEvent;
import com.vaadin.client.ResourceLoader.ResourceLoadEvent;
import com.vaadin.client.ResourceLoader.ResourceLoadListener;
import com.vaadin.client.communication.CommunicationProblemHandler;
import com.vaadin.client.communication.DefaultCommunicationProblemHandler;
import com.vaadin.client.communication.Heartbeat;
import com.vaadin.client.communication.ReconnectingCommunicationProblemHandler;
import com.vaadin.client.communication.RpcManager;
import com.vaadin.client.communication.ServerCommunicationHandler;
import com.vaadin.client.communication.ServerMessageHandler;
@@ -366,7 +366,7 @@ public class ApplicationConnection implements HasHandlers {
serverRpcQueue = GWT.create(ServerRpcQueue.class);
serverRpcQueue.setConnection(this);
communicationProblemHandler = GWT
.create(DefaultCommunicationProblemHandler.class);
.create(ReconnectingCommunicationProblemHandler.class);
communicationProblemHandler.setConnection(this);
serverMessageHandler = GWT.create(ServerMessageHandler.class);
serverMessageHandler.setConnection(this);

+ 4
- 0
client/src/com/vaadin/client/communication/DefaultCommunicationProblemHandler.java View File

@@ -44,6 +44,10 @@ public class DefaultCommunicationProblemHandler implements
this.connection = connection;
}

protected ApplicationConnection getConnection() {
return connection;
}

public static Logger getLogger() {
return Logger.getLogger(DefaultCommunicationProblemHandler.class
.getName());

+ 49
- 0
client/src/com/vaadin/client/communication/ReconnectDialog.java View File

@@ -0,0 +1,49 @@
/*
* Copyright 2000-2014 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 com.google.gwt.core.shared.GWT;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.user.client.ui.HTMLPanel;
import com.google.gwt.user.client.ui.Label;
import com.vaadin.client.ui.VOverlay;

/**
*
*
* @since 7.6
* @author Vaadin Ltd
*/
public class ReconnectDialog extends VOverlay {
interface MyUiBinder extends UiBinder<HTMLPanel, ReconnectDialog> {
}

private static MyUiBinder uiBinder = GWT.create(MyUiBinder.class);

@UiField
public Label label;

public ReconnectDialog() {
super(false, true);
addStyleName("v-reconnect-dialog");
setWidget(uiBinder.createAndBindUi(this));
}

public void setText(String text) {
label.setText(text);
}
}

+ 12
- 0
client/src/com/vaadin/client/communication/ReconnectDialog.ui.xml View File

@@ -0,0 +1,12 @@
<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'
xmlns:g='urn:import:com.google.gwt.user.client.ui'>

<g:HTMLPanel>

<g:Label ui:field="label" styleName="text"
text="Server connection lost, trying to reconnect..." />
<div class="spinner" />
</g:HTMLPanel>

</ui:UiBinder>


+ 181
- 0
client/src/com/vaadin/client/communication/ReconnectingCommunicationProblemHandler.java View File

@@ -0,0 +1,181 @@
/*
* Copyright 2000-2014 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 com.google.gwt.core.shared.GWT;
import com.google.gwt.http.client.Request;
import com.google.gwt.http.client.Response;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.ui.PopupPanel.PositionCallback;

import elemental.json.JsonObject;

//FIXME This is just a test and should be merged with DCPH
public class ReconnectingCommunicationProblemHandler extends
DefaultCommunicationProblemHandler {

private enum Type {
HEARTBEAT, XHR;
}

ReconnectDialog reconnectDialog = GWT.create(ReconnectDialog.class);
int reconnectAttempt = 0;
private Type reconnectionCause = null;;

@Override
public void xhrException(CommunicationProblemEvent event) {
handleTemporaryError(Type.XHR, event.getPayload());
}

@Override
public boolean heartbeatException(Request request, Throwable exception) {
handleTemporaryError(Type.HEARTBEAT, null);
return true;
}

@Override
public boolean heartbeatInvalidStatusCode(Request request, Response response) {
if (response.getStatusCode() == Response.SC_GONE) {
// Session expired
resolveTemporaryError(Type.HEARTBEAT, false);
return super.heartbeatInvalidStatusCode(request, response);
}

handleTemporaryError(Type.HEARTBEAT, null);
return true;
}

@Override
public void heartbeatOk() {
resolveTemporaryError(Type.HEARTBEAT, true);
}

private void handleTemporaryError(Type type, final JsonObject payload) {
reconnectAttempt++;
reconnectionCause = type;
if (!reconnectDialog.isAttached()) {
// FIXME
reconnectDialog.setStyleName("active", true);
reconnectDialog.setOwner(getConnection().getUIConnector()
.getWidget());
reconnectDialog.setPopupPositionAndShow(new PositionCallback() {
@Override
public void setPosition(int offsetWidth, int offsetHeight) {
// FIXME
reconnectDialog.setPopupPosition(0, 0);
}
});
}
if (payload != null) {
getConnection().getServerCommunicationHandler().endRequest();
}

if (reconnectAttempt >= getMaxReconnectAttempts()) {
// FIXME Remove
reconnectDialog.setText("Server connection lost. Gave up after "
+ reconnectAttempt + " attempts.");
// FIXME
reconnectDialog.setStyleName("active", false);

getConnection().setApplicationRunning(false);

} else {
reconnectDialog
.setText("Server connection lost, trying to reconnect... Attempt "
+ reconnectAttempt);

// Here and not in timer to avoid TB for getting in between
if (payload != null) {
// FIXME: Not like this
getConnection().getServerCommunicationHandler().startRequest();
}

// Reconnect
new Timer() {
@Override
public void run() {
if (payload != null) {
getLogger().info(
"Re-sending last message to the server...");
getConnection().getServerCommunicationHandler().send(
payload);
} else {
// Use heartbeat
getLogger().info(
"Trying to re-establish server connection...");
getConnection().getHeartbeat().send();
}
}
}.schedule(getReconnectInterval());
}
}

/**
* @since
* @return
*/
private int getMaxReconnectAttempts() {
// FIXME Parameter
return 15;
}

/**
* @since
* @return
*/
private int getReconnectInterval() {
// FIXME Parameter
return 5000;
}

@Override
public void xhrInvalidContent(CommunicationProblemEvent event) {
super.xhrInvalidContent(event);
};

@Override
public void xhrInvalidStatusCode(CommunicationProblemEvent event) {
handleTemporaryError(Type.XHR, event.getPayload());
}

@Override
public void xhrOk() {
resolveTemporaryError(Type.XHR, true);
}

private void resolveTemporaryError(Type cause, boolean success) {
if (reconnectionCause == null) {
// Not trying to reconnect
return;
}
if (reconnectionCause != cause) {
// If a heartbeat goes through while we are trying to re-send an
// XHR, we wait for the XHR to go through
return;
}

reconnectionCause = null;
if (reconnectDialog.isAttached()) {
reconnectDialog.hide();
}

if (success && reconnectAttempt != 0) {
getLogger().info("Re-established connection to server");
reconnectAttempt = 0;
}

}
}

+ 14
- 1
uitest/src/com/vaadin/tests/requesthandlers/CommunicationError.java View File

@@ -15,6 +15,9 @@
*/
package com.vaadin.tests.requesthandlers;

import java.io.IOException;
import java.io.PrintWriter;

import com.vaadin.launcher.ApplicationRunnerServlet;
import com.vaadin.server.CustomizedSystemMessages;
import com.vaadin.server.SystemMessages;
@@ -69,7 +72,17 @@ public class CommunicationError extends UIProvider {

@Override
public void buttonClick(ClickEvent event) {
VaadinService.getCurrentResponse().setStatus(400);
try {
// An unparseable response will cause
// communication error
PrintWriter writer = VaadinService
.getCurrentResponse().getWriter();
writer.write("for(;;)[{FOOBAR}]");
writer.flush();
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
});
addComponent(button);

Loading…
Cancel
Save