Skip to content

Commit 155b8cd

Browse files
authored
Merge branch 'main' into dependabot/npm_and_yarn/flow-tests/test-frontend/vite-context-path/vite-6.0.13
2 parents 8d65c01 + 5a59efa commit 155b8cd

File tree

15 files changed

+360
-25
lines changed

15 files changed

+360
-25
lines changed

flow-client/src/main/java/com/vaadin/client/communication/DefaultConnectionStateHandler.java

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@
2222
import com.google.gwt.user.client.Timer;
2323
import com.google.gwt.xhr.client.XMLHttpRequest;
2424

25-
import com.vaadin.client.Console;
2625
import com.vaadin.client.ConnectionIndicator;
26+
import com.vaadin.client.Console;
2727
import com.vaadin.client.Registry;
2828
import com.vaadin.client.UILifecycle;
2929
import com.vaadin.client.UILifecycle.UIState;
@@ -228,12 +228,18 @@ protected void scheduleReconnect(final JsonObject payload) {
228228
// do not need to start a new one
229229
if (reconnectAttempt == 1) {
230230
// Try once immediately
231+
Console.debug("Immediate reconnect attempt for " + payload);
231232
doReconnect(payload);
232233
} else {
233234
scheduledReconnect = new Timer() {
234235
@Override
235236
public void run() {
237+
if (scheduledReconnect != null) {
238+
scheduledReconnect.cancel();
239+
}
236240
scheduledReconnect = null;
241+
Console.debug("Scheduled reconnect attempt "
242+
+ reconnectAttempt + " for " + payload);
237243
doReconnect(payload);
238244
}
239245
};
@@ -259,11 +265,13 @@ protected void doReconnect(JsonObject payload) {
259265
return;
260266
}
261267
if (payload != null) {
262-
Console.debug("Re-sending last message to the server...");
263-
registry.getMessageSender().send(payload);
268+
Console.debug("Trying to re-establish server connection (UIDL)...");
269+
registry.getRequestResponseTracker()
270+
.fireEvent(new ReconnectionAttemptEvent(reconnectAttempt));
264271
} else {
265272
// Use heartbeat
266-
Console.debug("Trying to re-establish server connection...");
273+
Console.debug(
274+
"Trying to re-establish server connection (heartbeat)...");
267275
registry.getHeartbeat().send();
268276
}
269277
}
@@ -448,6 +456,10 @@ private void resolveTemporaryError(Type type) {
448456

449457
reconnectionCause = null;
450458
reconnectAttempt = 0;
459+
if (scheduledReconnect != null) {
460+
scheduledReconnect.cancel();
461+
scheduledReconnect = null;
462+
}
451463
ConnectionIndicator.setState(ConnectionIndicator.CONNECTED);
452464

453465
Console.debug("Re-established connection to server");

flow-client/src/main/java/com/vaadin/client/communication/MessageSender.java

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
import com.google.gwt.core.client.GWT;
2222
import com.google.gwt.user.client.Timer;
23+
2324
import com.vaadin.client.ConnectionIndicator;
2425
import com.vaadin.client.Console;
2526
import com.vaadin.client.Registry;
@@ -83,6 +84,17 @@ public enum ResynchronizationState {
8384
public MessageSender(Registry registry) {
8485
this.registry = registry;
8586
this.pushConnectionFactory = GWT.create(PushConnectionFactory.class);
87+
this.registry.getRequestResponseTracker()
88+
.addReconnectionAttemptHandler(ev -> {
89+
Console.debug(
90+
"Re-sending queued messages to the server (attempt "
91+
+ ev.getAttempt() + ") ...");
92+
// Try to reconnect by sending queued messages.
93+
// Stops the resend timer, since it will anyway not make any
94+
// request during reconnection process.
95+
resetTimer();
96+
doSendInvocationsToServer();
97+
});
8698
}
8799

88100
/**
@@ -128,7 +140,12 @@ private void doSendInvocationsToServer() {
128140
registry.getRequestResponseTracker().startRequest();
129141
sendPayload(payload);
130142
return;
131-
} else if (hasQueuedMessages() && resendMessageTimer == null) {
143+
} else if (hasQueuedMessages()) {
144+
Console.debug("Sending queued messages to server");
145+
if (resendMessageTimer != null) {
146+
// Stopping resend timer and re-send immediately
147+
resetTimer();
148+
}
132149
sendPayload(messageQueue.get(0));
133150
return;
134151
}
@@ -212,6 +229,12 @@ public void send(final JsonObject payload) {
212229
// been already sent and enqueued.
213230
if (!payload.hasKey(ApplicationConstants.SERVER_SYNC_ID)) {
214231
messageQueue.add(payload);
232+
Console.debug(
233+
"Message not sent because other messages are pending. Added to the queue: "
234+
+ payload.toJson());
235+
} else {
236+
Console.debug("Message not sent because already queued: "
237+
+ payload.toJson());
215238
}
216239
return;
217240
}
@@ -255,7 +278,6 @@ private void sendPayload(final JsonObject payload) {
255278
} else {
256279
Console.debug("send XHR");
257280
registry.getXhrConnection().send(payload);
258-
259281
resetTimer();
260282
// resend last payload if response hasn't come in.
261283
resendMessageTimer = new Timer() {
@@ -264,11 +286,16 @@ public void run() {
264286
resendMessageTimer
265287
.schedule(registry.getApplicationConfiguration()
266288
.getMaxMessageSuspendTimeout() + 500);
289+
// Avoid re-sending the message if a request is still in
290+
// progress.
291+
// If the response to the message has not yet been processed
292+
// the reconnection attempt listener takes care of resending
293+
// the queued message.
267294
if (!registry.getRequestResponseTracker()
268295
.hasActiveRequest()) {
269296
registry.getRequestResponseTracker().startRequest();
297+
registry.getXhrConnection().send(payload);
270298
}
271-
registry.getXhrConnection().send(payload);
272299
}
273300
};
274301
resendMessageTimer.schedule(registry.getApplicationConfiguration()
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/*
2+
* Copyright 2000-2025 Vaadin Ltd.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
5+
* use this file except in compliance with the License. You may obtain a copy of
6+
* the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations under
14+
* the License.
15+
*/
16+
package com.vaadin.client.communication;
17+
18+
import com.google.web.bindery.event.shared.Event;
19+
20+
import com.google.gwt.event.shared.EventHandler;
21+
22+
/**
23+
* Event fired when a reconnection attempt is requested.
24+
*
25+
* @author Vaadin Ltd
26+
* @since 24.7
27+
*/
28+
public class ReconnectionAttemptEvent
29+
extends Event<ReconnectionAttemptEvent.Handler> {
30+
31+
/**
32+
* Handler for {@link ReconnectionAttemptEvent}s.
33+
*/
34+
@FunctionalInterface
35+
public interface Handler extends EventHandler {
36+
/**
37+
* Called when handling of a reconnection attempt starts.
38+
*
39+
* @param event
40+
* the event object
41+
*/
42+
void onReconnectionAttempt(ReconnectionAttemptEvent event);
43+
}
44+
45+
private static Type<Handler> type = null;
46+
47+
private final int attempt;
48+
49+
/**
50+
* Creates an event object.
51+
*/
52+
public ReconnectionAttemptEvent(int attempt) {
53+
this.attempt = attempt;
54+
}
55+
56+
/**
57+
* Gets the number of the current reconnection attempt.
58+
*
59+
* @return the number of the current reconnection attempt.
60+
*/
61+
public int getAttempt() {
62+
return attempt;
63+
}
64+
65+
/**
66+
* Gets the type of the event after ensuring the type has been created.
67+
*
68+
* @return the type for the event
69+
*/
70+
public static Type<Handler> getType() {
71+
if (type == null) {
72+
type = new Type<>();
73+
}
74+
return type;
75+
}
76+
77+
@Override
78+
public Type<Handler> getAssociatedType() {
79+
return type;
80+
}
81+
82+
@Override
83+
protected void dispatch(Handler handler) {
84+
handler.onReconnectionAttempt(this);
85+
}
86+
87+
}

flow-client/src/main/java/com/vaadin/client/communication/RequestResponseTracker.java

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,15 @@
1515
*/
1616
package com.vaadin.client.communication;
1717

18-
import com.google.gwt.core.client.Scheduler;
1918
import com.google.web.bindery.event.shared.Event;
2019
import com.google.web.bindery.event.shared.EventBus;
2120
import com.google.web.bindery.event.shared.HandlerRegistration;
2221

23-
import com.vaadin.client.communication.MessageSender.ResynchronizationState;
22+
import com.google.gwt.core.client.Scheduler;
23+
2424
import com.vaadin.client.ConnectionIndicator;
2525
import com.vaadin.client.Registry;
26+
import com.vaadin.client.communication.MessageSender.ResynchronizationState;
2627
import com.vaadin.client.gwt.com.google.web.bindery.event.shared.SimpleEventBus;
2728

2829
/**
@@ -174,4 +175,16 @@ public HandlerRegistration addResponseHandlingEndedHandler(
174175
handler);
175176
}
176177

178+
/**
179+
* Adds a handler for {@link ReconnectionAttemptEvent}s.
180+
*
181+
* @param handler
182+
* the handler to add
183+
* @return a registration object which can be used to remove the handler
184+
*/
185+
public HandlerRegistration addReconnectionAttemptHandler(
186+
ReconnectionAttemptEvent.Handler handler) {
187+
return eventBus.addHandler(ReconnectionAttemptEvent.getType(), handler);
188+
}
189+
177190
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
* Copyright 2000-2025 Vaadin Ltd.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
5+
* use this file except in compliance with the License. You may obtain a copy of
6+
* the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations under
14+
* the License.
15+
*/
16+
package com.vaadin.flow.misc.ui;
17+
18+
import com.vaadin.flow.component.UI;
19+
import com.vaadin.flow.component.html.Div;
20+
import com.vaadin.flow.component.html.NativeButton;
21+
import com.vaadin.flow.component.html.Span;
22+
import com.vaadin.flow.router.Route;
23+
24+
@Route("slow-response")
25+
public class SlowResponseView extends Div {
26+
27+
public static final String SLOW_ADD = "slowAdd";
28+
public static final String ADD = "add";
29+
public static final String ADDED_PREDICATE = "added_";
30+
31+
private int elements = 0;
32+
33+
public SlowResponseView() {
34+
int messageTimeoutMillis = UI.getCurrent().getSession().getService()
35+
.getDeploymentConfiguration().getMaxMessageSuspendTimeout();
36+
add(new Span("Max message suspend timeout: " + messageTimeoutMillis));
37+
NativeButton slowAddElement = new NativeButton(
38+
"Add element (slow response)", event -> {
39+
slowAddElement(messageTimeoutMillis + 1000);
40+
});
41+
slowAddElement.setId(SLOW_ADD);
42+
43+
NativeButton addElement = new NativeButton("Add element", event -> {
44+
addElement();
45+
});
46+
addElement.setId(ADD);
47+
48+
add(slowAddElement, addElement);
49+
}
50+
51+
private void addElement() {
52+
Div addedElement = new Div("Added element");
53+
addedElement.setId(ADDED_PREDICATE + elements++);
54+
add(addedElement);
55+
}
56+
57+
private void slowAddElement(long delayMillis) {
58+
try {
59+
Thread.sleep(delayMillis);
60+
} catch (InterruptedException e) {
61+
Thread.currentThread().interrupt();
62+
throw new RuntimeException(e);
63+
}
64+
addElement();
65+
}
66+
}

0 commit comments

Comments
 (0)