| /* |
| * Copyright 2011 Google Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| * use this file except in compliance with the License. You may obtain a copy of |
| * the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| * License for the specific language governing permissions and limitations under |
| * the License. |
| */ |
| package com.google.web.bindery.requestfactory.gwt.client; |
| |
| import com.google.gwt.core.client.Scheduler; |
| import com.google.gwt.core.client.Scheduler.ScheduledCommand; |
| import com.google.web.bindery.requestfactory.shared.Receiver; |
| import com.google.web.bindery.requestfactory.shared.RequestContext; |
| import com.google.web.bindery.requestfactory.shared.RequestFactory; |
| import com.google.web.bindery.requestfactory.shared.impl.AbstractRequestContext; |
| |
| /** |
| * A RequestBatcher is a convenience class that allows RequestFactory operations |
| * to be aggregated over a single tick of the event loop and sent as one HTTP |
| * request. Instances of RequestBatcher are reusable, so they may be used as |
| * application-wide singleton objects or within a particular subsystem or UI. |
| * <p> |
| * Subclasses need only to provide the instance of the RequestFactory used by |
| * the application and a method to provide a "default" RequestContext from the |
| * RequestFactory. |
| * |
| * <pre> |
| * public class MyRequestBatcher extends RequestBatcher<MyRequestFactory, MyRequestContext> { |
| * public MyRequestBatcher() { |
| * // MyRequestFactory could also be injected |
| * super(GWT.create(MyRequestFactory.class)); |
| * } |
| * |
| * protected MyRequestContext createContext(MyRequestFactory factory) { |
| * return factory.myRequestContext(); |
| * } |
| * } |
| * </pre> |
| * A singleton or otherwise scoped instance of RequestBatcher should be injected |
| * into consuming classes. The {@link RequestContext#fire()} and |
| * {@link com.google.web.bindery.requestfactory.shared.Request#fire() |
| * Request.fire()} methods reachable from the RequestContext returned from |
| * {@link #get()} will not trigger an HTTP request. The |
| * {@link RequestContext#fire(Receiver)} and |
| * {@link com.google.web.bindery.requestfactory.shared.Request#fire(Receiver) |
| * Request.fire(Receiver)} methods will register their associated Receivers as |
| * usual. This allows consuming code to be written that can be used with or |
| * without a RequestBatcher. |
| * <p> |
| * When an application uses multiple RequestContext types, the |
| * {@link RequestContext#append(RequestContext)} method can be used to chain |
| * multiple RequestContext objects together: |
| * |
| * <pre> |
| * class MyRequestBatcher { |
| * public OtherRequestContext otherContext() { |
| * return get().append(getFactory().otherContext()); |
| * } |
| * } |
| * </pre> |
| * |
| * @param <F> the type of RequestFactory |
| * @param <C> any RequestContext type |
| * @see Scheduler#scheduleFinally(ScheduledCommand) |
| */ |
| public abstract class RequestBatcher<F extends RequestFactory, C extends RequestContext> { |
| private C openContext; |
| private AbstractRequestContext openContextImpl; |
| private final F requestFactory; |
| |
| protected RequestBatcher(F requestFactory) { |
| this.requestFactory = requestFactory; |
| } |
| |
| /** |
| * Returns a mutable {@link RequestContext}. |
| */ |
| public C get() { |
| return get(null); |
| } |
| |
| /** |
| * Returns a mutable {@link RequestContext} and enqueues the given receiver to |
| * be called as though it had been passed directly to |
| * {@link RequestContext#fire(Receiver)}. |
| */ |
| public C get(Receiver<Void> receiver) { |
| if (openContext == null) { |
| openContext = createContext(requestFactory); |
| openContextImpl = (AbstractRequestContext) openContext; |
| openContextImpl.setFireDisabled(true); |
| getScheduler().scheduleFinally(new ScheduledCommand() { |
| @Override |
| public void execute() { |
| assert !openContextImpl.isLocked() : "AbstractRequestContext.fire() should have been a no-op"; |
| openContextImpl.setFireDisabled(false); |
| openContext.fire(); |
| openContext = null; |
| openContextImpl = null; |
| } |
| }); |
| } |
| if (receiver != null) { |
| // Queue a final callback receiver |
| openContextImpl.fire(receiver); |
| } |
| return openContext; |
| } |
| |
| /** |
| * Convenience access to the RequestFactory instance to aid developers using |
| * multiple RequestContext types. |
| * |
| * <pre> |
| * RequestBatcher{@literal <MyRequestFactory, MyRequestContext>} batcher; |
| * |
| * public void useOtherRequestContext() { |
| * OtherRequestContext ctx = batcher.get().append(batcher.getFactory().otherContext()); |
| * ctx.someOtherMethod().to(someReceiver); |
| * } |
| * </pre> |
| */ |
| public F getRequestFactory() { |
| return requestFactory; |
| } |
| |
| /** |
| * Subclasses must implement this method to return an instance of a |
| * RequestContext. |
| */ |
| protected abstract C createContext(F requestFactory); |
| |
| /** |
| * Returns {@link Scheduler#get()}, but may be overridden for testing |
| * purposes. |
| */ |
| protected Scheduler getScheduler() { |
| return Scheduler.get(); |
| } |
| } |