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 ProviderT 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 PresenterProxyT 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 QueueCommand queue; private ProxyView view; public PresenterProxy(HandlerManager bus) { this(bus, false); } public PresenterProxy(HandlerManager bus, boolean eager) { this.bus = bus; this.queue = new LinkedListCommand(); 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 PresenterProxyProfilePresenter { private ProviderProfilePresenter provider; @Inject
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 AsyncShimMyPresenter { } 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. On Tue, Feb 9, 2010 at 8:42 AM, jarrod jarrod.carl...@gmail.com wrote: 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 ProviderT 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 PresenterProxyT 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 QueueCommand queue; private ProxyView view; public PresenterProxy(HandlerManager bus) { this(bus, false); } public PresenterProxy(HandlerManager bus, boolean eager) { this.bus = bus; this.queue = new LinkedListCommand(); 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. *
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: 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 AsyncShimMyPresenter { } 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. On Tue, Feb 9, 2010 at 8:42 AM, jarrod jarrod.carl...@gmail.com wrote: 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 ProviderT 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 PresenterProxyT 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 QueueCommand queue; private ProxyView view; public PresenterProxy(HandlerManager bus) { this(bus, false); } public PresenterProxy(HandlerManager bus, boolean eager) { this.bus = bus; this.queue = new LinkedListCommand(); 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
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. On Mon, Jan 25, 2010 at 8:16 PM, jarrod jarrod.carl...@gmail.com wrote: 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 ProxyPresenterT 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 ProviderT provider; private QueueCommand queue; private ProxyView view; public ProxyPresenter(HandlerManager bus, ProviderT provider) { this(bus, provider, false); } public ProxyPresenter(HandlerManager bus, ProviderT provider, boolean eager) { this.bus = bus; this.provider = provider; this.queue = new LinkedListCommand(); 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, ProviderRealPresenter provider) { return new ProxyPresenterRealPresenter(bus, provider); } The rest is automagic! -- You received this message because you are subscribed to the Google Groups Google Web Toolkit group. To post to this group, send email to google-web-tool...@googlegroups.com. To unsubscribe from this group, send email to