2

How can I show/call a dialog programmatically and add this to stage (actual browser window)?

I want to trigger a database update every 2 hours. I have done this with a TimerTask. This works fine for me, the timer task gets all the data I want from the database. Before this timer task is triggered I want to "lock" the screen for some seconds that no user (session scoped) can access the database (I also know how this will work). My problem is that I don't know/cannot find a way to call a dialog programmatically.


Update I want to set this primefaces dialog:

Dialog dialog = new Dialog(); 
dialog.setAppendToBody(true);
dialog.setModal(true);
dialog.setVisible(true);
dialog.setWidgetVar("generatedDialog");
dialog.setId("fancyDialog");
dialog.setClosable(false);
dialog.setHeader("Getting latest information from the database");
dialog.setDynamic(true);
dialog.setResizable(false);
dialog.setDraggable(false);

How can I display it to my browser?

Cœur
  • 37,241
  • 25
  • 195
  • 267
Sway
  • 151
  • 1
  • 3
  • 7

3 Answers3

1

You don't need to create a dialog programatically. What you need is Push Technlogogy, i.e. the server initiates the interaction with the clients. PrimeFaces already have this technology and ready to use for you.

A basic case for your needs would be the PrimePush - FacesMessage:

  • User writes a notification.
  • The server gets the notification.
  • The notification is sent to the PushContext under the /notifications channel.
  • To every client that contains the /notifications channel, an action will be executed. In this case, the channel is only in the same page, and the action will be to show the notification.

You can test this behavior by opening the same page in two different navigators and send a notification. All the pages will show the notification (and looks like what you want).

Having this example on, the only thing you need to do will be:

  1. Set a channel on every page (this is really tedious work but it's what you want/need) or on the master pages (in case you used the template system).
  2. Your timer has to invoke somewhat a Server request to start the notification. There's a sample here about how to upload a file programmatically, but you only have to start the request1, no need to send any parameters.
  3. The request that the Timer invokes will add a notification to the channel.
  4. The channel will automatically fire the action and can do what you want/need. In the provided example, it shows a growl, but you can modify it to show a <p:dialog>.

1 In order to prevent users to invoke this request for you, it would be good to set a Filter that prevents anyone except your timer to execute the request.

Community
  • 1
  • 1
Luiggi Mendoza
  • 85,076
  • 16
  • 154
  • 332
  • Hmm ... I dont get it at the moment ... Your explanation to this sounds good for me, but ... (some kind of "wtf" :D) Ok ... Could you help me a bit more ? I am using PrimeFaces 4.0 ... I dont have any "PushContext" :/ To your point 1. : Do you mean that I have to set everywhere that "growl, socket and javascript"-thingy ? To your point 2. : I dont get it :/ can you give me another example ?! To your point 3. : I get it that I have to add a notification to the channel... To your point4. : I am with you that I have to call in the javascript thingy a my dialog I only want to display my dialog :D – Sway Nov 03 '12 at 18:32
  • First at all, you're using PrimeFaces 3.4, there's no PrimeFaces 4 yet (at least I'm from the present, not the future). Very strange that you don't have access to the `PushContext` class (it's in the PrimeFaces framework jar). In 1, yes I say that you have to place it everywhere in your project (that's what you want, don't you? To show the message to every user and doesn't matter which page the user is using atm). In 2, I'll try to see if there's another way (AFAIK that's the way). 3 and 4 are covered and it looks like you understood them well :). – Luiggi Mendoza Nov 03 '12 at 19:25
  • I don't get the PushContext thing running ... I added it to the (AFAIK) right places, but nothing happens ... I will post the code what I did! – Sway Nov 03 '12 at 20:46
  • I added a new answer what i did. – Sway Nov 03 '12 at 21:21
0

There is an easy solution:

  1. Firstly add a dialog to master page. This dialog's rendered attribute should firstly be set to false.

  2. Secondly Refresh current page following codes

    protected void refreshPage() { 
        FacesContext fc = FacesContext.getCurrentInstance(); 
        String refreshpage = fc.getViewRoot().getViewId();
    
        ViewHandler ViewH = fc.getApplication().getViewHandler(); 
        UIViewRoot UIV = ViewH.createView(fc,refreshpage);
        UIV.setViewId(refreshpage); 
        fc.setViewRoot(UIV); 
    }
    
  3. Thirdly call above method with TimerTask class related method and set the dialog rendered attribute to true in this TimerTask class' related method.

Thats all :)

olyanren
  • 1,448
  • 4
  • 24
  • 42
0

According to @Luiggi Mendoza idea, i am stuck a bit ...

I added to my jsf the following:

<ui:composition xmlns="http://www.w3.org/1999/xhtml"
    xmlns:ui="http://java.sun.com/jsf/facelets"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:p="http://primefaces.org/ui"
    xmlns:f="http://java.sun.com/jsf/core"
    template="/templates/layoutUser.xhtml">

    <ui:define name="content">

        <p:growl widgetVar="growl" showDetail="true" />

        <h:form>
        Fanciest Page ever          
        </h:form>

        <p:socket onMessage="handleMessage" channel="/notifications" />

        <script type="text/javascript">
            function handleMessage(facesmessage) {
                facesmessage.severity = 'info';

                growl.show([ facesmessage ]);
            }
        </script>

    </ui:define>
</ui:composition>

I am calling my TimerTask in a bean which is sessionscoped:

@PostConstruct
    public void initLazyModel() {
        getTimerMB().startTimer(this);
        System.out.println("Timer started");

    }

My Timer looks like this:

@ManagedBean(name = "timerMB")
@SessionScoped
public class TimerManagedBean extends TimerTask {

    private int counter;
    private Timer timer;
    private ArtikelManagedBean artikelMB;

    public void startTimer(ArtikelManagedBean artikelMB){
        this.artikelMB = artikelMB;
        this.timer = new Timer(); 
        Calendar date = Calendar.getInstance();  
        date.set(2012, 3, 28, 21, 28);
        //execute every 10 seconds  
        timer.schedule(this, date.getTime(), 10000);
    }

    @PreDestroy
    public void cancelTimer(){
        this.timer.cancel();
        System.out.println("UpdateTimer cancelled!");
    }

    @Override
    public void run() {
        counter++;
        System.out.println("Upadatetimer started: "+ new Date()+" ("+counter+")");
            PushContext pushContext = PushContextFactory.getDefault()
            .getPushContext();

    pushContext.push("/notifications", new FacesMessage("Simple",
            "Test"));
    }
}

Hmm ... Nothing happens, but anything should happen, shouldn't it ? It should give me an notification with "simple" and "test", i guess...

UPDATE:

Thank you Luiggi Mendoza! Managed it in a very simple way to show my dialogs (call it serverside). I added the following Servlet to my web.xml.

<servlet>
    <servlet-name>Push Servlet</servlet-name>
    <servlet-class>org.primefaces.push.PushServlet</servlet-class>

    <init-param>
    <param-name>org.atmosphere.useBlocking</param-name>
    <param-value>true</param-value>
</init-param>

    <init-param>
        <param-name>org.atmosphere.cpr.sessionSupport</param-name>
        <param-value>true</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>Push Servlet</servlet-name>
    <url-pattern>/primepush/*</url-pattern>
</servlet-mapping>
Sway
  • 151
  • 1
  • 3
  • 7
  • That's because you haven't started a request in your `TimerManagedBean#run`. Also, I though you wanted this behavior for all the application users at the same time, if this were the case, the bean should be `@ApplicationScoped`. – Luiggi Mendoza Nov 03 '12 at 22:15
  • Thanks for your answer! I want this to be "@SessionScoped", because every logged user should get a database update after "one hour". Thats my plan, do you think this will work with "@SessionScoped" ? Furthermore, I am stuck at the moment to start a request ... How this should look like, can you give me an example/hint ?! Thank you very much for answering me :) – Sway Nov 03 '12 at 22:25
  • if you want all the users receive the notification at the same time, the bean should be `@ApplicationScoped`, if you have a `@SessionScoped` every user will receive the notificacion after 1 hour the bean has been initialized **per session*. More about this [here](http://balusc.blogspot.com/2011/09/communication-in-jsf-20.html). About how to start a request, check what I've said in the point 2 of my answer. – Luiggi Mendoza Nov 03 '12 at 23:09
  • Thank you very much, that is what I need with the "@SessionScoped"-thing. I will try the request thing. Updates will come if I have found a solution. Again, thank you for your help, really like it. – Sway Nov 03 '12 at 23:47