1

I have an ICEfaces web app which contains a component with a property linked to a backing bean variable. In theory, variable value is programmatically modified, and the component sees the change and updates its appearance/properties accordingly.

However, it seems that the change in variable isn't "noticed" by the component until the end of the JSF cycle (which, from my basic understanding, is the render response phase).

The problem is, I have a long file-copy operation to perform, and I would like the the inputText component to show a periodic status update. However, since the component is only updated at the render response phase, it doesn't show any output until the Java methods have finished executing, and it shows it all changes accumulated at once.

I have tried using FacesContext.getCurrentInstance().renderResponse() and other functions, such as PushRenderer.render(String ID) to force XmlHttpRequest to initialize early, but no matter what, the appearance of the component does not change until the Java code finishes executing.

One possible solution that comes to mind is to have an invisible button somewhere that is automatically "pressed" by the bean when step 1 of the long operation completes, and by clicking it, it calls step 2, and so on and so forth. It seems like it would work, but I don't want to spend time hacking together such an inelegant solution when I would hope that there is a more elegant solution built into JSF/ICEfaces.

Am I missing something, or is resorting to ugly hacks the only way to achieve the desired behavior?

4

2 回答 2

4

多线程是缺少的链接,与 PushRenderer 和 PortableRenderer 结合使用(请参阅http://wiki.icesoft.org/display/ICE/Ajax+Push+-+APIs)。

现在,我的支持 bean 中有三个线程——一个用于执行长操作,一个用于轮询状态,一个“主”线程用于生成新线程并将 UI 控制返回给客户端浏览器。

一旦主线程启动执行和轮询线程,它就会终止并完成原始 HTTP 请求。我的 PortableRenderer 被声明为PortableRender portableRenderer;并且在我的 init() 方法(由类构造函数调用)中包含:

PushRenderer.addCurrentSession("fullFormGroup");    
portableRenderer = PushRenderer.getPortableRenderer();

对于线程部分,我implements Runnable在我的类中使用,并且为了在单个类中处理多个线程,我遵循了这个 StackOverflow 帖子:如何在一个类中处理多个线程?

这是一些源代码。我无法透露我使用的明确源代码,但这是一个简化版本,不会透露任何机密信息。我还没有测试过它,我在 gedit 中编写了它,所以它可能有一两个语法错误,但它至少应该让你朝着正确的方向开始。

public void init()
{
    // This method is called by the constructor.
    // It doesn't matter where you define the PortableRenderer, as long as it's before it's used.
    PushRenderer.addCurrentSession("fullFormGroup");
    portableRenderer = PushRenderer.getPortableRenderer();
}   


public void someBeanMethod(ActionEvent evt)
{
    // This is a backing bean method called by some UI event (e.g. clicking a button)
    // Since it is part of a JSF/HTTP request, you cannot call portableRenderer.render

    copyExecuting = true;

    // Create a status thread and start it

    Thread statusThread = new Thread(new Runnable() {
        public void run() {
        try {
                        // message and progress are both linked to components, which change on a portableRenderer.render("fullFormGroup") call
            message = "Copying...";
            // initiates render. Note that this cannot be called from a thread which is already part of an HTTP request
            portableRenderer.render("fullFormGroup"); 
            do {
                progress = getProgress();
                portableRenderer.render("fullFormGroup"); // render the updated progress
                Thread.sleep(5000); // sleep for a while until it's time to poll again
            } while (copyExecuting);
            progress = getProgress();
            message = "Finished!";
            portableRenderer.render("fullFormGroup"); // push a render one last time
        } catch (InterruptedException e) {
            System.out.println("Child interrupted.");
        }
    });
    statusThread.start();

    // create a thread which initiates script and triggers the termination of statusThread
    Thread copyThread = new Thread(new Runnable() {           
        public void run() {
        File someBigFile = new File("/tmp/foobar/large_file.tar.gz");
            scriptResult = copyFile(someBigFile); // this will take a long time, which is why we spawn a new thread
            copyExecuting = false; // this will caue the statusThread's do..while loop to terminate


        } 
    });
    copyThread.start();
}
于 2012-06-05T19:54:30.033 回答
0

我建议查看我们的展示演示:

http://icefaces-showcase.icesoft.org/showcase.jsf?grp=aceMenu&exp=progressBarBean

在进度条示例列表下是一个名为 Push 的示例。它使用 Ajax Push(ICEfaces 提供的一项功能)来做我认为你想要的。

此页面上还有一个名为 Easy Ajax Push 的教程,它将引导您完成一个使用 Ajax Push 的简单示例。

http://www.icesoft.org/community/tutorials-samples.jsf

于 2012-06-05T15:29:00.560 回答