0

Story:

I'm working on a accommodation website. I want to do some Ajax render in a page based on day change. If i use a JSF command component like command button, the WebSocket Push will work. But if i use observable bean to ask observer bean to invoke the pushContext.send method, the onDayChange method get called after one minute and there is no exception in this method but the accommodationSearchFields element isn't rendered and thus the data-currentDate attribute doesn't change and when i look at Web Developer Tools > Network > XHR, there is no record.

What I've done:

The EJB Bean:

@Singleton @Lock(LockType.READ)
public class DateScheduled implements NextDayObservable{
    // Other member variables
    private final ScheduledExecutorService scheduledExecutorService;
    private final List<NextDayObserver> dateObservers;

    public DateScheduled(){
        /*this.now = LocalDateTime.now();
        this.nextDay = LocalDateTime.now()
                                    .withHour(00)
                                    .withMinute(0)
                                    .withSecond(1)
                                    .withNano(0);
       scheduledExecutorService.scheduleAtFixedRate(
            () -> { notifyObserversOfDayChange(); },
            now.until(nextDay, ChronoUnit.MILLIS),
            TimeUnit.DAYS.toMillis(1),
            TimeUnit.MILLISECONDS
        );*/        
        this.scheduledExecutorService = Executors.newScheduledThreadPool(1);
        scheduledExecutorService.scheduleAtFixedRate(
            () -> { notifyObserversOfDayChange(); },
            10000,
            TimeUnit.DAYS.toMillis(1),
            TimeUnit.MILLISECONDS
        );
        this.dateObservers = new ArrayList<>();
    }

    @Override @Lock(LockType.WRITE)
    public void registerObserver(final NextDayObserver dateObserver){
        if(dateObservers.indexOf(dateObserver) < 0){
            dateObservers.add(dateObserver);
        }
    }

    @Override @Lock(LockType.WRITE)
    public void removeObserver(final NextDayObserver dateObserver){
        dateObservers.remove(dateObserver);
    }

    @Override
    public void notifyObserversOfDayChange(){
        dateObservers.forEach(
            dateObserver -> { dateObserver.onDayChange(); }
        );
    }
}

The Backing Bean:

@Named @ApplicationScoped
public class MainTemplateMB extends BaseManagedBean implements Serializable, NextDayObserver{
    private static final long serialVersionUID = 1L;

    // Other member variables
    
    private String currentDate;
    
    @EJB
    protected NextDayObservable nextDayObservable;
    
    @Inject @Push
    private PushContext pushContext;
    
    // Other member variables
    
    public MainTemplateMB(){
        // Other Initializations
        this.currentDate = "";
    }

    @PostConstruct
    private void initialization(){
        // Other Initializations
        nextDayObservable.registerObserver(this);
        currentDate = DateUtility.getCurrentYear() + "." + (DateUtility.getCurrentMonth() -1) + "." + DateUtility.getCurrentDay() + "." + DateUtility.getMonthFirstDay()+ "." + DateUtility.getLastMonthMaximumDays();
    }

    // Other Methods
    
    @Override
    public void onDayChange(){
        // currentDate has changed for testing
        currentDate = DateUtility.getCurrentYear() + "." + ( DateUtility.getCurrentMonth() + 1 ) + "." + DateUtility.getCurrentDay() + "." + DateUtility.getMonthFirstDay()+ "." + DateUtility.getLastMonthMaximumDays();
        pushContext.send("dayChange");
    }
}

The MainTemplate.xhtml:

<?xml version="1.0" encoding="UTF-8" ?>

<!DOCTYPE html>

<html
    xmlns="http://www.w3.org/1999/xhtml"
    xmlns:jsf="http://xmlns.jcp.org/jsf"
    xmlns:jsfc="http://xmlns.jcp.org/jsf/core"
    xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
    xmlns:h="http://xmlns.jcp.org/jsf/html"
    lang="#{sessionTools.language}" xml:lang="#{sessionTools.language}">
    
    <jsfc:view locale="#{sessionTools.locale}" contentType="text/html">
        <head>
            .
            .
            .
            
        </head>

        <body>
            <div id="wrapper">
                .
                .
                .

                <section jsf:id="accommodationSearchFields" data-currentDate="#{mainTemplateMB.currentDate}">
                    <form jsf:id="accommodationSearchForm" method="GET" autocomplete="off">
                        .
                        .
                        .
                        <input class="button field roundedCorners4" type="submit" value="#{mainTemplateProp.search}" jsf:action="#{mainTemplateMB.goToTheCity()}" />
                <!--  <input class="button field roundedCorners4" type="submit" value="#{mainTemplateProp.search}" jsf:action="#{mainTemplateMB.onDayChange()}">
                            <jsfc:ajax />
                        </input> -->
                        .
                        .
                        .
                    </form>
                </section>
                .
                .
                .


            </div>
            <form jsf:id="dayChangeWebSocket">
                <jsfc:websocket channel="pushContext">
                    <jsfc:ajax event="dayChange" render=":accommodationSearchFields" />
                </jsfc:websocket>
            </form>
        </body>
    </jsfc:view>
</html>

After switching to JEE Observer Pattern:

The EJB Bean:

@Singleton @Startup @Lock(LockType.READ)
public class DateScheduled{
    // Other member variables
    private final ScheduledExecutorService scheduledExecutorService;

    @Inject
    private BeanManager beanManager;

    public DateScheduled(){
        /*this.now = LocalDateTime.now();
        this.nextDay = LocalDateTime.now()
                                    .withHour(00)
                                    .withMinute(0)
                                    .withSecond(1)
                                    .withNano(0);
       scheduledExecutorService.scheduleAtFixedRate(
            () -> { notifyObserversOfDayChange(); },
            now.until(nextDay, ChronoUnit.MILLIS),
            TimeUnit.DAYS.toMillis(1),
            TimeUnit.MILLISECONDS
        );*/        
        this.scheduledExecutorService = Executors.newScheduledThreadPool(1);
        scheduledExecutorService.scheduleAtFixedRate(
            () -> { notifyObserversOfDayChange(); },
            60000,
            TimeUnit.DAYS.toMillis(1),
            TimeUnit.MILLISECONDS
        );
    }

    public void notifyObserversOfDayChange(){
        beanManager.fireEvent("Hello");
    }
}

The Backing Bean:

@Named @ApplicationScoped
public class MainTemplateMB extends BaseManagedBean implements Serializable{
    private static final long serialVersionUID = 1L;

    // Other member variables

    private String currentDate;


    @Inject @Push
    private PushContext pushContext;

    // Other member variables

    public MainTemplateMB(){
        // Other Initializations
        this.currentDate = "";
    }

    @PostConstruct
    private void initialization(){
        // Other Initializations
        currentDate = DateUtility.getCurrentYear() + "." + (DateUtility.getCurrentMonth() -1) + "." + DateUtility.getCurrentDay() + "." + DateUtility.getMonthFirstDay()+ "." + DateUtility.getLastMonthMaximumDays();
    }

    // Other Methods

    public void onDayChange(@Observes String message){
        // currentDate has changed for testing
        currentDate = DateUtility.getCurrentYear() + "." + ( DateUtility.getCurrentMonth() + 1 ) + "." + DateUtility.getCurrentDay() + "." + DateUtility.getMonthFirstDay()+ "." + DateUtility.getLastMonthMaximumDays();
        pushContext.send("dayChange");
    }
}

Work Tools:

. Apache TomEE Plus 8.0.3

. Apache NetBeans IDE 12.0

Questions:

  1. Did i something wrong?
  2. What is the problem?
  3. How to solve the problem?
Arash
  • 696
  • 8
  • 24
  • Strange approach of observers. It's not terribly clear from your question which step exactly failed. Have you already debugged it? In any case, is this example helpful? https://stackoverflow.com/q/3787514 – BalusC Aug 06 '20 at 21:12
  • Hi @BlausC and thanks. Yes. No. I've read this post before. Observable and observer work as I expected. *currentDate* is changed and `pushContext.send("dayChange")` get Called after 10 seconds. But the *accommodationSearchFields* element isn't rendered and thus the *data-currentDate* attribute doesn't change. – Arash Aug 07 '20 at 04:37
  • Are you saying that standard JEE observers as demonstrated in the link in my previous comment didn't work either? You're not following the same pattern but manually passing around physical instances (which are actually proxies) across JEE boundaries and this won't necessarily work and it's also a poor practice. Please adjust your code to follow the same pattern as in the given link and rinse and repeat. – BalusC Aug 07 '20 at 08:22
  • No, I'm not saying that. I didn't use *JEE Observer Pattern*. I used this simple manually implemented observer pattern as i used to. I'll follow your instruction and tell you the result, Thanks. – Arash Aug 07 '20 at 14:40
  • @BlausC, I changed it to *JEE Observer Pattern*. But the problem remains. **proxies** means, instances in *dateObservers* list aren't managed by CDI? (I changed the question) – Arash Aug 08 '20 at 15:00

1 Answers1

0

I solved the problem.

When i added onopen client event to <f:websocket>, i realized that the socket doesn't open. I came across this excellent answer while searching: https://stackoverflow.com/a/2120183/3960315.

cause number 8:

If you're using Ajax via JSF 2.x <f:ajax> or e.g. PrimeFaces <p:commandXxx>, make sure that you have a <h:head> in the master template instead of the <head>. Otherwise JSF won't be able to auto-include the necessary JavaScript files which contains the Ajax functions. This would result in a JavaScript error like "mojarra is not defined" or "PrimeFaces is not defined" in browser's JS console.

Now i see that i must use <h:head> to solve the problem. Thanks to BalusC.

But i don't understand why clicking the command button works but invoking the observer method doesn't? Given that i didn't see any JavaScript error in the console.

Arash
  • 696
  • 8
  • 24