Re: Large MVP applications and GWT.runAsync()

2010-02-09 Thread jarrod
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()

2010-02-09 Thread Joe Cheng
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()

2010-02-09 Thread jarrod
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()

2010-02-08 Thread Joe Cheng
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