Zkoss:如何通知用户会话超时或与服务器的连接丢失

Zkoss: How to notify user of session timeout or lost connection to server?

本文关键字:超时 服务器 连接 会话 用户 何通知 通知 Zkoss      更新时间:2023-09-26

在使用zkoss web UI框架时,是否有一种编程检查会话超时或连接丢失的方法?问题是,即使我关闭了服务器、重新部署或重新启动它,UI也会一直显示在浏览器中。

用户只有在尝试与UI交互(例如单击按钮)时才会发现问题。它要么弹出会话超时信息,要么弹出服务器无法访问信息。

我正在寻找的是客户端能够立即(或在合理短的时间内)通知用户的东西。例如,当会话丢失、服务器关闭或无法访问时,会显示一个弹出窗口,"告诉"用户有什么问题(这样他就可以立即刷新或采取其他操作)。

我在看医生,但他们没有说任何具体的情况。会话设置包括限制,保持活动以防止会话超时,但仅此而已

我正在考虑一些定制的javascript,它会定期ping服务器(也许是一个特殊的servlet,它也会让脚本知道会话超时),但我更喜欢zk框架的原生版本。当连接丢失时,zk计时器组件似乎停止了功能,但它不会触发错误弹出窗口。

我稍后将发布代码,说明如何为所有登录的客户端分发消息。

同时,以下是您可以实现的代码,以便在会话超时即将激活之前通知用户(不用于服务器重启):

SessionTimeOutExecListener.java:

public class SessionTimeOutExecListener implements ExecutionInit, UiLifeCycle {
    private String timerId = "timeoutNotifyTimer";
    public void init(Execution exec, Execution parent) throws Exception {
        Timer timer = (Timer) exec.getDesktop().getAttribute(timerId);
        if (timer != null) {
            if (isSendbyMsgBox(timer, exec))
                return;
            timer.stop();
            timer.start();
        }
    }
    private boolean isSendbyMsgBox(Timer timer, Execution exec) {
        HttpServletRequest hreq = (HttpServletRequest)exec.getNativeRequest();
        for (int j = 0;; ++j) {
            final String uuid = hreq.getParameter("uuid_"+j);
            if (uuid == null)
                break;
            if (uuid.equals(timer.getUuid()))
                return true;
        }
        return false;
    }
    public void afterComponentAttached(Component comp, Page page) {
    }
    public void afterComponentDetached(Component comp, Page prevpage) {
    }
    public void afterComponentMoved(Component parent, Component child,
            Component prevparent) {
    }
    public void afterPageAttached(Page page, Desktop desktop) {
         Object obj = desktop.getAttribute(timerId);
        if (obj == null) {
            int tmout = desktop.getWebApp().getConfiguration()
                    .getSessionMaxInactiveInterval();
            final Timer timer = new Timer((tmout - 2) * 1000);
            timer.addEventListener(Events.ON_TIMER, new EventListener() {
                public void onEvent(Event event) throws Exception {
                    Messagebox.show("Your session are about to be expired",
                        "Information", Messagebox.OK,
                        Messagebox.INFORMATION, new EventListener() {
                            @Override
                            public void onEvent(Event event) throws Exception {
                                if (Messagebox.ON_OK.equals(event.getName())){
                                    timer.start();
                                }
                            }
                        });
                }
            });
            timer.setPage(page);
            desktop.setAttribute(timerId, timer);
            timer.start();
        }
    }
    public void afterPageDetached(Page page, Desktop prevdesktop) {
    }
}

zk.xml:

<listener>
    <listener-class>test.MyExecListener</listener-class>
</listener>

编辑:

正如我所说,这是用于向所有活动会话发送消息的代码
首先是一个可以发送消息的简单页面。

message.zul:

<?xml version="1.0" encoding="UTF-8"?>
<window xmlns="http://www.zkoss.org/2005/zul" apply="org.zkoss.bind.BindComposer"
            viewModel="@id('vm') @init('be.chillworld.MessageVM')" width="100%" height="100%">
    <textbox value="@bind(vm.message)"/>
    <button onClick="@command('sendMessage')" label="Send"/>
</window>

正如您所看到的,它是MVVM,但不要担心,您也可以将其插入MVC项目中,因为我使用EventQueue

然后,我们需要所有页面的监听器,因此最佳实践是创建一个抽象VM或扩展您使用的Composer。

AbstractVM.java:

package be.chillworld;
import org.zkoss.bind.annotation.Init;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zk.ui.event.EventQueues;
import org.zkoss.zk.ui.util.Clients;
/**
 *
 * @author chillworld
 */
public abstract class AbstractVM {
    @Init
    public void abstractInit() {
        EventQueues.lookup("globalMessage", EventQueues.APPLICATION, true).subscribe(new EventListener<Event>() {
            public void onEvent(Event event) throws Exception {
                if ("onSendMessage".equals(event.getName())) {
                    Clients.showNotification(String.valueOf(event.getData()));
                }
            }
        });
    }
}

因此,当我们得到活动时,这实际上就是我们要做的
如果你想制作一个基本的作曲家,你可以这样做:

BasicComposer.java:

package be.chillworld;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zk.ui.event.EventQueues;
import org.zkoss.zk.ui.select.SelectorComposer;
import org.zkoss.zk.ui.util.Clients;
/**
 *
 * @author chillworld
 */
public class BasicComposer extends SelectorComposer<Component> {
    @Override
    public void doAfterCompose(Component comp) throws Exception {
        super.doAfterCompose(comp);
        EventQueues.lookup("globalMessage", EventQueues.APPLICATION, true).subscribe(new EventListener<Event>() {
            public void onEvent(Event event) throws Exception {
                if ("onSendMessage".equals(event.getName())) {
                    Clients.showNotification(String.valueOf(event.getData()));
                }
            }
        });
    }
}

不要忘记为每个视图扩展BasicComposerAbstractVM
这就是为什么我问你是否使用页面模板,如果你为每个页面都有一个主模板,你只需将其插入其中,每个页面都会订阅eventlistener,所以你不必使用BasicComposerAbstractVM

好的,现在我们为message.zul.创建视图模型

消息VM.java:

package be.chillworld;
import org.zkoss.bind.annotation.Command;
import org.zkoss.bind.annotation.Init;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventQueues;
import org.zkoss.zk.ui.util.Clients;
/**
 *
 * @author chillworld
 */
public class MessageVM extends AbstractVM {
    private String message;
    @Init(superclass = true)
    public void init(){}
    @Command
    public void sendMessage () {
        if (message==null || message.trim().isEmpty()) {
            Clients.showNotification("Please enter something to send.");
        } else {
            System.out.println("posting event");
            EventQueues.lookup("globalMessage", EventQueues.APPLICATION, true).publish(new Event("onSendMessage", null, message));
        }
    }

    public String getMessage() {
        return message;
    }
    public void setMessage(String message) {
        this.message = message;
    }
}

正如您所看到的,我将扩展AbstractVM,但有1个部分非常重要然后只扩展它,即@Init
正如您所看到的,我再次声明了一个init方法,但我在注释中说的是superclass = true
如果我们不这样做,来自abstractVM的init方法将永远不会被触发

对于BasicComposer,这是不同的,如果覆盖doAfterCompose方法,则始终调用super,以便始终执行。

你可以在1台电脑上用多个会话进行测试,如下所示:

  • 打开IE=>转到message.zul
  • 在private中打开IE=>转到订阅了eventlistener的某个页面
  • 私下打开FF=>与IE相同
  • 在文本框中输入信息,然后按send
  • 查看是否所有浏览器都收到消息。(如果您想确定,请将String.valueOf(Sessions.getCurrent())放在Clients.showNotification中)

希望这能帮助你解决问题。