想要向GlassFish Server添加计时器,不知道从哪里开始

时间:2017-05-30 16:12:58

标签: java timer websocket server glassfish

我正在尝试使用Glassfish服务器实现Java EE websockets,而且我已经掌握了基础知识。

至于我目前的项目,我想在游戏中实现服务器“滴答”,每隔几毫秒更新一次服务器,以便连接到服务器的所有会话看到相同的东西。

但是,我不知道从哪里开始使用Glassfish。我知道我从头开始创建一个服务器并实现一个计时器方法,但如果可能的话,我宁愿在这里保存一些工作。

简单地说,在glassfish服务器中实现定时器/滴答机制是否容易(假设它正在运行)?

例如,我想让服务器在通过websocket连接后定期向客户端HTML页面发送单词“hello”。

1 个答案:

答案 0 :(得分:1)

短版

我们用三个类来解决它:

  1. 计划程序类:发送事件的时钟
  2. Websocket端点:基本的websocket会话处理程序
  3. Websocket会话处理程序:捕获事件并管理所有websocket会话的指挥
  4. 带代码的长版本

    现在代码中,1)是Mike在评论中提到的:一个典型的调度程序,可以触发一些事件。这个课程基本上是一个时钟,无论谁正在听它的事件。我们有一个EAR = EJB + WAR项目。我们的计时器在EJB模块中,但是如果你有一个WAR,你可以把它放在那里。

    // some import
    import javax.enterprise.event.Event;
    
    @Singleton
    @Startup
    public class TimerBean {
    
        @Inject
        private Event<TickEvent> tickEvent;
    
        @Schedule(hour = "*", minute = "*", second = "*/5")
        public void printSchedule() {
            tickEvent.fire(new TickEvent(/*...your initialization...*/));
        }
    
        public class TickEvent{
            // we have some info here but you can 
            // leave it empty.
        }
    
    }
    

    对于2)websocket端点,这是一个非常学术的终点。不要忘记一个连接=一个端点实例。您可以使用session.getOpenedSessions()找到来自给定会话的所有已打开会话,但我将在下一部分解释为何使用处理程序。

    // some import
    import javax.websocket.OnClose;
    import javax.websocket.OnError;
    import javax.websocket.OnMessage;
    import javax.websocket.OnOpen;
    import javax.websocket.Session;
    
    @ServerEndPoint(value = "/myEndPointPath")
    public class TickEndPoint{
    
        @Inject
        private TickSessionHandler sessionHandler;
    
        @OnOpen
        public void onOpen(Session session){
            sessionHandler.addSession(session);
        }
    
        @OnClose
        public void onClose(Session session, CloseReason reason){
             sessionHandler.removeSession(session);
        }
    
    }
    

    现在是keystone:会话处理程序。会话处理程序是ApplicationScoped bean,因此它始终可用。我使用它的原因是:

    1. 当触发TickEvent时,您不知道是否存在TickEndPoint实例,因此事件捕获可能会引发一些愚蠢的错误。我们用@Observes(notifyObserver = Reception.IF_EXISTS)修复了它,但我们遇到了其他EJB问题,我将在2中详细说明。
    2. 由于我尚未确定的原因,当我在@ServerEndPoint中使用@Observes时,我的所有@EJB注入都失败了所以我将@EJB注入切换为@Inject。但是由于ApplicationScoped是一个CDI bean,@ EJB和@Observes在会话处理程序类中可以很好地协同工作。我们本身并不需要@EJB注释,但为了与其他bean的设计一致性,我们希望所有的EJB都使用@EJB进行注释。
    3. 监控:您可以进行监控,例如您在时间T发送了多少“你好”,给你时间与连接会话数量图表。
    4. 另外,在我们的特殊情况下,我们有一些观点,您可能会在以后见面。

      1. 我们需要选择SESSIONS的特定子集,而不是所有子集。在@ServerEndPoint中拥有一个静态Set导致了一些东西 - 这让我变成了Hulk行为,特别是当你有EJB依赖时。在一个游戏的例子中,让我们用蓝队和红队捕捉旗帜情景。如果红队抓住了旗帜,你需要向蓝队发送“哎呀,我们失去了旗帜”,并向红队发送“是的,我们得到了它”。
      2. 监控:除了连接时间和内容之外,我们需要诸如“上次活动时间”和内容之类的信息。要将其正确放入JSF数据表中,CDI bean中的列表/集是最佳选择
      3. 我遇到了一些教程/详细信息,其中@ServerEndPoint被注释为@ApplicationScoped。按设计(一个服务器端点=一个websocket连接),我觉得不舒服所以我拒绝实施这个解决方案

        @Named
        @ApplicationScoped
        public class TickSessionHandler{
        
            // There is no need to have a static Set, worst,
            // containers don't like it
            public Set<Session> SESSIONS;
        
            // let's initialize the set
            public TickSessionHandler{
                this.SESSIONS = new HashSet<>();
            }
        
            // ---------- sessions management
            public void addSession(Session session){
                this.SESSIONS.add(session);
            }
        
            public void removeSession(Sesssion session){
                this.SESSIONS.remove(session);
            }
        
            // ---------- Listen to the timer
            public void onTick(@Observes TickEvent event){
        
                // if required, get the event attribute
                // and proceed
        
                // your request:
                this.SESSIONS.forEach(session -> {
                    session.getBasicRemote().sendText("hello");
                });
            }
        
        }
        
      4. 希望这有帮助。解释似乎很长,但核心实现实际上非常轻。 AFAIK,这种要求没有捷径。