2

我有一个启动应用程序,我正在向其中添加一些杂物屏幕。我决定使用 Vaadin,在我将它部署到多节点生产环境之前,它似乎工作得很好。一旦进入 prod 环境,屏幕就会无缘无故地不断刷新。例如,在一个屏幕中有一个网格,当单击一行时,会弹出一个对话框,显示项目的详细信息。但是一旦弹出对话框,页面就会刷新无数次。

此论坛主题https://vaadin.com/forum/thread/17586129/routerlayout-causing-page-refresh是我正在使用的相同布局的示例,并描述了一个非常相似的问题。

我有一个扩展 VerticalLayout 的基本抽象类,所有具体类都扩展了这个基本抽象类。每个具体类都定义了自己的路由并使用一个通用的布局类。

我已经联系了 gitter、vaadin 论坛并在 github 中打开了一个错误,但据我所知,没有来自 Vaadin 的人想要回应任何事情。

以下是我正在使用的所有版本: Vaadin Flow 版本:14 Java 版本:12.0.1+12 操作系统版本:Mac 10.14.5 浏览器版本:Fire Fox 70.0.1,Chrome 78.0.3904.97

我的实现中的代码片段:主视图

@Slf4j
@RoutePrefix("v1/crud")
@Theme(value = Material.class, variant = Material.DARK)
public class MainView extends Div implements RouterLayout {
private H1 h1 = new H1("Vaadin Crud UI");
private HorizontalLayout header = new HorizontalLayout(h1);
private Div content = new Div();
private ApplicationContext context;

@Inject
public MainView(ApplicationContext context) {
    this.context = context;
    setSizeFull();
    h1.setWidthFull();
    content.setWidthFull();
    header.setWidthFull();
    header.setAlignItems(FlexComponent.Alignment.CENTER);
    VerticalLayout navigationBar = new VerticalLayout();
    navigationBar.setWidth("25%");

    navigationBar.add(createNavigationButton("Home", VaadinIcon.HOME, ReportTab.class));
    navigationBar.add(createNavigationButton("Batch Search", VaadinIcon.SEARCH, BatchSearchTab.class));
    ... a bunch more buttons
    HorizontalLayout layout = new HorizontalLayout(navigationBar, content);
    layout.setWidthFull();
    VerticalLayout page = new VerticalLayout(header, layout);
    page.setWidthFull();
    add(page);
}

@Override
public void showRouterLayoutContent(HasElement hasElement) {
    if (hasElement != null) {
        Element newElement = hasElement.getElement();
        if (newElement != null) {
            content.removeAll();
            content.getElement().appendChild(newElement);
        }
    }
}

private Button createNavigationButton(String caption, VaadinIcon icon, Class<? extends BaseEditor> editor) {
    Button button = new Button(caption, icon.create());
    button.addClickListener(event -> UI.getCurrent().navigate(editor));
    button.addThemeVariants(ButtonVariant.MATERIAL_CONTAINED);
    button.getStyle().set("background-color", "#00819D");
    button.setWidthFull();
    return button;
}

}

基础组件:

@Slf4j
@Data
@SpringComponent
@UIScope
public abstract class BaseEditor<P, B> extends VerticalLayout {

private final RememberMeService rememberMe;
private final P businessProcess;
protected Binder<B> binder;
protected Dialog editDialog = new Dialog();
protected Button save = new Button("Save", VaadinIcon.CHECK.create());
protected Button close = new Button("Close", VaadinIcon.EXIT.create());
protected Button delete = new Button("Delete", VaadinIcon.TRASH.create());
protected B bean;

private ChangeHandler changeHandler;
private boolean proceed = true;

public BaseEditor(P businessProcess, RememberMeService rememberMe) {
    this.rememberMe = rememberMe;
    this.businessProcess = businessProcess;
    save.addClickListener(e -> save());
    delete.addClickListener(e -> delete());
}

public abstract void delete();

public abstract void save();

protected abstract Component getContent();

protected void edit(B e) {
    bean = e;
    editDialog.open();
    getBinder().setBean(e);
}

protected void initEditorPanel(Component... components) {
    HorizontalLayout actions = new HorizontalLayout(save, close, delete);
    VerticalLayout data = new VerticalLayout(components);
    data.add(actions);
    editDialog.removeAll();
    editDialog.add(data);
    getBinder().bindInstanceFields(this);
    close.addClickListener(e -> editDialog.close());
}

public interface ChangeHandler {
    void onChange();
}

void setChangeHandler(ChangeHandler h) {
    changeHandler = h;
}

void errorDialog(String message) {
    final Button close = new Button("Close", VaadinIcon.CLOSE.create());
    H3 h3 = new H3(message);
    final Dialog errorDialog = new Dialog(h3, close);
    errorDialog.open();
    close.addClickListener(e -> errorDialog.close());
}

BaseEditor filter(Predicate<B> predicate) {
    Objects.requireNonNull(predicate);
    proceed = predicate.test(bean);
    return this;
}

void buttonConsumer(Consumer<B> consumer) {
    if (!proceed) {
        proceed = true;
        return;
    }
    try {
        consumer.accept(bean);
    } catch (Exception e) {
        errorDialog(e.getMessage());
    } finally {
        editDialog.close();
        getChangeHandler().onChange();
    }
}

void either(Consumer<B> whenTrue, Consumer<B> whenFalse) {
    try {
        if (proceed) {
            whenTrue.accept(bean);
        } else {
            whenFalse.accept(bean);
        }
    } catch (Exception e) {
        errorDialog(e.getMessage());
    } finally {
        proceed = true;
        editDialog.close();
        getChangeHandler().onChange();
    }
}
}

混凝土构件:

@Slf4j
@Route(value = "search/batch", layout = MainView.class)
public class BatchSearchTab extends BaseEditor<BatchService, Batch> {
private TextField searchField1;
private TextField searchField2;

public BatchSearchTab(BatchService businessProcess, RememberMeService rememberMe) {
    super(businessProcess, rememberMe);
    binder = new Binder<>(Batch.class);
    save.setIcon(VaadinIcon.REPLY.create());
    save.setText("Replay");
    delete.setIcon(VaadinIcon.CLOSE.create());
    delete.setText("Cancel");
    getContent();
}

@Override
public void delete() {
    buttonConsumer(b -> getBusinessProcess().cancelBatch(b.getBatchId(), b.getUserAgent()));
}

@Override
public void save() {
    filter(b -> b.isReplayable()).buttonConsumer(b -> getBusinessProcess().buildAndSendFile((getBean())));
}

@Override
public void edit(Batch batch) {
    HorizontalLayout actions = new HorizontalLayout();
    H2 h2 = new H2();
    if (batch.isReplayable()) {
        h2.setText("Would you like to replay the following.");
        actions.add(save, delete, close);
    } else {
        h2.setText("This record is not eligible for replay.");
        actions.add(close);
    }

    Label batchId = new Label("Correlation Id: " + batch.getBatchId());
    Label txnCount = new Label("Transaction Count: " + batch.getTotalTxns());
    Label txnAmount = new Label("Total: " + batch.getTotalBatchAmount());
    VerticalLayout data = new VerticalLayout(h2, batchId, txnCount, txnAmount, actions);
    data.add(actions);
    editDialog.removeAll();
    editDialog.add(data);
    close.addClickListener(e -> editDialog.close());
    editDialog.open();
    getBinder().setBean(batch);
}

@Override
protected Component getContent() {
    final H2 h2 = new H2("Locate Batches");
    searchField1 = new TextField("Batch Code");
    searchField2 = new TextField("User Agent");
    searchField2.addKeyPressListener(Key.ENTER, e -> keyPressListener());
    Button searchBtn = new Button("Search", VaadinIcon.SEARCH.create());
    HorizontalLayout search = new HorizontalLayout(searchField1, searchField2);
    searchBtn.addClickListener(e -> {
        search(searchField1.getValue(), searchField2.getValue());
    });
    add(h2, search, searchBtn);
    return this;
}

private void search(String code, String userAgent) {
    log.info("Searching {} and {}", code, userAgent);
    List<Batch> batches =
        getBusinessProcess().getBatchesForUserAgent(code, userAgent, 60);
    log.info("Found {} batches", batches.size());
    if (batches.size() > 0) {
        buildGrid(batches, "BatchId", "totalTxns", "totalBatchAmount", "status");
    } else {
        errorDialog("No Records found for criteria");
    }
}

private void keyPressListener() {
    String code = StringUtils.isNotBlank(searchField1.getValue()) ? searchField1.getValue() : null;
    if (StringUtils.isNotBlank(searchField2.getValue())) {
        search(code, searchField2.getValue());
    }
}

private void buildGrid(Collection<Batch> records, String... columns) {
    Component result;
    if (records.size() == 0) {
        result = new Label("NO REPORT DATA AVAILABLE.");
    } else {
        final Grid<Batch> grid = new Grid<>(Batch.class);
        grid.setHeightByRows(records.size() < 10);
        grid.setColumns(columns);
        grid.setItems(records);
        grid.setWidthFull();
        grid.asSingleSelect().addValueChangeListener(l -> Optional.ofNullable(l.getValue()).ifPresent(this::edit));
        result = grid;
    }
    if (getComponentCount() < 3) {
        add(result);
    } else {
        replace(getComponentAt(2), result);
    }
}

private void loadData(String code, String userAgent) {
    if (StringUtils.isNotBlank(code)) {
        search(null, userAgent);
    } else {
        search(code, userAgent);
    }
}
}
4

1 回答 1

1

免责声明:通过 IRC 发生了一些进一步的事实调查

该问题的答案与 OP 在循环负载均衡器后面运行应用程序的多个实例有关。客户端访问随机服务器,因此没有会​​话在那里运行。

对此的解决方案是拥有一个共享的会话存储,理想情况下让负载均衡器在现有会话上分派,这样“热”的后端服务器就会受到打击。

于 2019-11-26T09:09:00.563 回答