Account Options

  1. Sign in
The old Google Groups will be going away soon, but your browser is incompatible with the new version.
Google Groups Home
« Groups Home
Large MVP applications and GWT.runAsync()
There are currently too many topics in this group that display first. To make this topic appear first, remove this option from another topic.
There was an error processing your request. Please try again.
flag
  5 messages - Collapse all  -  Translate all to Translated (View all originals)
The group you are posting to is a Usenet group. Messages posted to this group will make your email address visible to anyone on the Internet.
Your reply message has not been sent.
Your post was successful
 
From:
To:
Cc:
Followup To:
Add Cc | Add Followup-to | Edit Subject
Subject:
Validation:
For verification purposes please type the characters you see in the picture below or the numbers you hear by clicking the accessibility icon. Listen and type the numbers you hear
 
jarrod  
View profile  
 More options Jan 25 2010, 11:16 pm
From: jarrod <jarrod.carl...@gmail.com>
Date: Mon, 25 Jan 2010 20:16:23 -0800 (PST)
Local: Mon, Jan 25 2010 11:16 pm
Subject: Large MVP applications and GWT.runAsync()
While building an application for my company, I needed a way to make
large sections of the application sit behind a split point. After
organizing my application into "modules" of related functionality, I
came up with slick, easy way to make those "modules" split out
automatically: by using a proxy presenter.

My application uses gin and a hand-made MVP framework based loosely
off of gwt-presenter. Some adaptation may be necessary to fit your
particular frameworks, but here goes:

public class ProxyPresenter<T extends Presenter> implements Presenter
{

    private static class ProxyView implements View {

        SimplePanel proxy = new SimplePanel();

        ProxyView() {

        }

        @Override
        public Widget asWidget() {
            return this.proxy;
        }

        protected void setView(View view) {
            this.proxy.setWidget(view.asWidget());
        }

    }

    private boolean asyncCalled;
    private boolean bound;

    private HandlerManager bus;
    private T impl;
    private Provider<T> provider;
    private Queue<Command> queue;
    private ProxyView view;

    public ProxyPresenter(HandlerManager bus, Provider<T> provider) {
        this(bus, provider, false);
    }

    public ProxyPresenter(HandlerManager bus, Provider<T> provider,
            boolean eager) {
        this.bus = bus;
        this.provider = provider;
        this.queue = new LinkedList<Command>();
        this.view = new ProxyView();
        if (eager) {
            ensurePresenter();
        }
    }

    @Override
    public void bind() {
        this.bound = true;
        queue(new Command() {

            @Override
            public void execute() {
                ProxyPresenter.this.impl.bind();
            }

        });
    }

    @Override
    public View getView() {
        return this.view;
    }

    @Override
    public void handleHistory(final HistoryItem item) {
        queue(new Command() {

            @Override
            public void execute() {
                ProxyPresenter.this.impl.handleHistory(item);
            }

        });
    }

    @Override
    public boolean isBound() {
        return this.bound;
    }

    @Override
    public void release() {
        queue(new Command() {

            @Override
            public void execute() {
                ProxyPresenter.this.impl.release();
            }
        });
        this.bound = false;
    }

    protected void ensurePresenter() {
        if (!this.asyncCalled) {
            this.asyncCalled = true;
            GWT.runAsync(new RunAsyncCallback() {

                @Override
                public void onFailure(Throwable reason) {
                    ProxyPresenter.this.bus
                            .fireEvent(new ApplicationExceptionEvent
(reason));
                }

                @Override
                public void onSuccess() {

                    // get impl instance
                    ProxyPresenter.this.impl =
ProxyPresenter.this.provider
                            .get();

                    // fill-in proxy view
                    ProxyPresenter.this.view.setView
(ProxyPresenter.this.impl
                            .getView());

                    // execute any queued commands
                    while (ProxyPresenter.this.queue.peek() != null) {
                        Command cmd = ProxyPresenter.this.queue.poll
();
                        cmd.execute();
                    }

                }
            });
        }
    }

    protected void queue(Command command) {
        ensurePresenter();
        if (this.impl != null) {
            command.execute();
        } else {
            this.queue.offer(command);
        }
    }

    T getPresenter() {
        return this.impl;
    }

}

Then, in my gin module, instead of using an explicit bind, I use a
@Provides method, like so:

    @Provides
    Presenter getRealPresenter(HandlerManager bus,
            Provider<RealPresenter> provider) {
        return new ProxyPresenter<RealPresenter>(bus, provider);
    }

The rest is automagic!


 
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Joe Cheng  
View profile  
 More options Feb 8 2010, 3:31 am
From: Joe Cheng <j...@joecheng.com>
Date: Mon, 8 Feb 2010 00:31:11 -0800
Local: Mon, Feb 8 2010 3:31 am
Subject: Re: Large MVP applications and GWT.runAsync()

Jarrod, are you using this ProxyPresenter more than once within the same
application (with different presenter type parameters), and found that it
introduced the split points as expected? I originally tried something like
this but found that it would only ever introduce a single split point, no
matter how many times I used it.

I was able to solve the problem using deferred binding and was about to
write a blog post about it, but if your code actually works as desired then
I need to go back and figure out what I was doing wrong.


 
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
jarrod  
View profile  
 More options Feb 9 2010, 11:42 am
From: jarrod <jarrod.carl...@gmail.com>
Date: Tue, 9 Feb 2010 08:42:35 -0800 (PST)
Local: Tues, Feb 9 2010 11:42 am
Subject: Re: Large MVP applications and GWT.runAsync()
Joe,

In fact, no it does not work properly with multiple instances, and
this is a problem I discovered shortly after originally posting this.
The solution I devised was to make the Proxy abstract and simply
create concrete sub-classes for each Presenter I want to proxy
(anonymous sub-classes do not work!). This is a little tedious, but
the abstraction I wrote makes it pretty simple to implement. Below is
the updated code. Note that I also changed the ViewProxy to use a
LayoutPanel instead of a SimplePanel so that proxied views that
implement RequiresLayout are supported.

/**
 * PresenterProxy wraps a Presenter implementation and acts as a
gateway to that
 * Presenter. The wrapped Presenter is created on-demand the first
time any
 * method is called, or optionally it can be created eagerly at the
time the
 * PresenterProxy is created.
 *
 * Instantiation of the wrapped Presenter occurs behind a
GWT.runAsync() call so
 * that complex wrapped Presenters may be split out into code
fragments during
 * module compilation.
 *
 * In order to achieve this "delayed" instantiation, the
PresenterProxy must be
 * given a Provider<T extends Presenter> instance to provide the
actual
 * implementation at the appropriate time.
 *
 * Additionally, a ViewProxy is utilized that consists of a
LayoutPanel to
 * minimize the impact on the UI. The ViewProxy can be inserted into
the DOM
 * immediately without waiting for the wrapped Presenter to become
available.
 *
 * Any calls made to the wrapped Presenter before the instance becomes
available
 * are queued in a buffer and later replayed when the instance becomes
 * available. Once available, all calls are executed immediately on
the wrapped
 * instance.
 *
 * @author jcarlson
 *
 * @param <T>
 */
public abstract class PresenterProxy<T extends Presenter> implements
        Presenter {

    private static class ProxyView implements View {

        LayoutPanel proxy = new LayoutPanel();

        ProxyView() {
        }

        @Override
        public Widget asWidget() {
            return this.proxy;
        }

        protected void setView(View view) {
            this.proxy.clear();
            this.proxy.add(view.asWidget());
        }

    }

    private boolean asyncCalled;
    private boolean bound;

    private HandlerManager bus;
    private T impl;
    private Queue<Command> queue;
    private ProxyView view;

    public PresenterProxy(HandlerManager bus) {
        this(bus, false);
    }

    public PresenterProxy(HandlerManager bus, boolean eager) {
        this.bus = bus;
        this.queue = new LinkedList<Command>();
        this.view = new ProxyView();
        if (eager) {
            ensurePresenter();
        }
    }

    @Override
    public final void bind() {
        this.bound = true;
        queue(new Command() {

            @Override
            public void execute() {
                PresenterProxy.this.impl.bind();
            }

        });
    }

    @Override
    public final View getView() {
        return this.view;
    }

    @Override
    public final void handleHistory(final HistoryItem item) {
        queue(new Command() {

            @Override
            public void execute() {
                PresenterProxy.this.impl.handleHistory(item);
            }

        });
    }

    @Override
    public final boolean isBound() {
        return this.bound;
    }

    @Override
    public final void release() {
        queue(new Command() {

            @Override
            public void execute() {
                PresenterProxy.this.impl.release();
            }
        });
        this.bound = false;
    }

    protected final void onAsyncFailure(Throwable reason) {
        PresenterProxy.this.bus
                .fireEvent(new ApplicationExceptionEvent(reason));
    }

    protected final void onAsyncSuccess(T impl) {
        // set impl instance
        this.impl = impl;

        // fill-in proxy view
        this.view.setView(PresenterProxy.this.impl.getView());

        // execute any queued commands
        while (ResizingPresenterProxy.this.queue.peek() != null) {
            Command cmd = PresenterProxy.this.queue.poll();
            cmd.execute();
        }

    }

    /**
     * The key method that subclasses must override.
     * This allows each GWT.runAsync() call to be in its own
     * concrete class, thus allowing the compiler to produce
     * multiple exclusive fragments.
     */
    protected abstract void runAsync();

    void ensurePresenter() {
        if (!this.asyncCalled) {
            this.asyncCalled = true;
            runAsync();
        }
    }

    void queue(Command command) {
        ensurePresenter();
        if (this.impl != null) {
            command.execute();
        } else {
            this.queue.offer(command);
        }
    }

}

And an implementation of the proxy:

public class MyPresenterProxy extends
        PresenterProxy<ProfilePresenter> {

    private Provider<ProfilePresenter> provider;

    @Inject
    public MyPresenterProxy(HandlerManager bus,
            Provider<ProfilePresenter> provider) {
        super(bus);
        this.provider = provider;
    }

    @Override
    protected void runAsync() {
        GWT.runAsync(new RunAsyncCallback() {

            @Override
            public void onFailure(Throwable reason) {
                onAsyncFailure(reason);
            }

            @Override
            public void onSuccess() {
                MyPresenter presenter = MyPresenterProxy.this.provider
                        .get();
                onAsyncSuccess(presenter);
            }
        });
    }

}

On Feb 8, 3:31 am, Joe Cheng <j...@joecheng.com> wrote:

...

read more »


 
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Joe Cheng  
View profile  
 More options Feb 9 2010, 4:14 pm
From: Joe Cheng <j...@joecheng.com>
Date: Tue, 9 Feb 2010 13:14:57 -0800
Local: Tues, Feb 9 2010 4:14 pm
Subject: Re: Large MVP applications and GWT.runAsync()

OK, great, I went through a similar approach previously. The new approach
I'm using is similar but you can remove the boilerplate in the proxy
implementation, as the deferred binding mechanism will fill it all in. In
fact since you're using Gin it will literally look like this:

public abstract class MyAsyncShim extends AsyncShim<MyPresenter> { }

I'll reply to this thread when I have something written up. I would also
like to adapt your idea of using a command queue, right now I'm doing
something similar but not guaranteeing the methods will be executed in the
order they were invoked.

...

read more »


 
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
jarrod  
View profile  
 More options Feb 9 2010, 7:32 pm
From: jarrod <jarrod.carl...@gmail.com>
Date: Tue, 9 Feb 2010 16:32:56 -0800 (PST)
Local: Tues, Feb 9 2010 7:32 pm
Subject: Re: Large MVP applications and GWT.runAsync()
Ah, well I'd be interested to see what you come up with as far as
reducing the boilerplate.

I had tried a few different scenarios, but I found that unless the
actual call to GWT.runAsync for each proxied Presenter was in a unique
class then the compiler wouldn't split out the code properly. I'm
guessing the code-splitting mechanism in the compiler pivots at least
in part on the class containing the call.

On Feb 9, 4:14 pm, Joe Cheng <j...@joecheng.com> wrote:

...

read more »


 
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
End of messages
« Back to Discussions « Newer topic     Older topic »