Limitations * Does not take Push into account * Hard coded parameters * Only theme for Valo Change-Id: Iddb12d20391bcd30dc7289b7ea694ac3fbbd116dtags/7.6.0.alpha5
@@ -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; | |||
} | |||
@@ -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; | |||
} | |||
} | |||
} |
@@ -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); |
@@ -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()); |
@@ -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); | |||
} | |||
} |
@@ -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> | |||
@@ -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; | |||
} | |||
} | |||
} |
@@ -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); |