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 <[email protected]> wrote:
> 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 <[email protected]> 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 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 received this message because you are subscribed to the Google Groups
> > "Google Web Toolkit" group.
> > To post to this group, send email to [email protected].
> > To unsubscribe from this group, send email to
> > [email protected]<google-web-toolkit%2Bunsubs
> > [email protected]>
> > .
> > For more options, visit this group at
> >http://groups.google.com/group/google-web-toolkit?hl=en.
--
You received this message because you are subscribed to the Google Groups
"Google Web Toolkit" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to
[email protected].
For more options, visit this group at
http://groups.google.com/group/google-web-toolkit?hl=en.