简体   繁体   中英

jsf 2 update UI from bean after delay + blinking

i'm not sure if what i try to accomplish is possible at all, so i'd appreciate any help. what i'm basically trying to do is create an online checkers game, where users can compete one another or the computer. the game's logic is inside EJB, and so far it works fine. so far i've accomplished the player-computer game, and it works fine - the player picks the move's origin and destination (while the logic decides if they're valid), and then the computer makes it move, afterward the board is updated. however, currently there are 2 issues i want to solve: 1) i want the computer's move to take place about a second after the player is done. it works - however the board isn't updated (i'll explain later why) 2) after the computer makes it move, i want the squares it affected to blink few times.

on my board, each square is a command button that looks like this:

<h:commandButton id="B1" image="#{CodeBean.imgSrc}"  actionListener="#{CodeBean.clickImg()}" action="#{CodeBean.update}"  onmouseover="this.src='#{CodeBean.overImgSrc}'" onmouseout="this.src='/CheckersClientSide#{CodeBean.imgSrc}'">
    <f:ajax render="@form"   />
</h:commandButton>

in my bean, the 'imgSrc' getter looks like this:

public String getImgSrc()
{
    FacesContext fc = FacesContext.getCurrentInstance();
    UIComponent c = UIComponent.getCurrentComponent(fc);
    String cID = c.getId();
    String output = "/resources/EMPTY_BOX.jpg";
    String currOption = gameBoard.get(cID);
    if (currOption != null)
    {
        output = "/resources/" + currOption + ".jpg";
    }
    return output;
  }

in the 'clickImg' function inside the bean, i check if any square was already picked, for otherwise i know the square was picked as the move's origin; otherwise, i call the EJB's move function, which returns an updated board map, afterwards i call the 'getImgSrc' function, which updates the board, and then i call the EJB's computer move's function, which also return the updated board's map, and then i call the 'getImgSrc' again, which update the board itself.
however, if i try to call the computer's move function using a timer, like this -

Timer timer = new Timer();
timer.schedule(new TimerTask()
{
   @Override
   public void run()
   {
      getMachineMove();
   }
} , 1000);

the 'getMachineMove' function is call after a second, and that function call the 'getImgSrc', but the board doesn't get updated. it doesn't get updated because 'FacesContext.getCurrentInstance()' returns null. i hope i was clear enough - and in case i was, i'd appreciate any kind of help (and also ideas how can i make certain command-buttons blink from the bean).

cheers, eRez

There are several serious conceptual mistakes.

First, you can only send a HTTP response to a HTTP request. You cannot send a HTTP response when there's no HTTP request. A HTTP request is only initiated by the client. The FacesContext is stored in the HTTP request thread. That explains why there's no FacesContext in your timer thread.

You could introduce HTTP push with websockets, but this is not exactly trivial. The PrimeFaces component library has a <p:push> component for that. But easier would be to just calculate the next move directly and let JSF print a line of JavaScript code which does the update job client side with setTimeout(callback, millis) .

Second, using Timer / TimerTask is an extremely bad idea in a supposedly life-long running Java EE web application. Never ever think about doing it once again later. You should be using the java.util.concurrent API or the server-provided task scheduler. This is not the answer to your quesiton (you already have it; use HTTP push or JS), but to learn more check Spawning threads in a JSF managed bean for scheduled tasks using a timer

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM