/* * AsyncShim.java * * Copyright (C) 2009-12 by RStudio, Inc. * * Unless you have received this program directly from RStudio pursuant * to the terms of a commercial license agreement with RStudio, then * this program is licensed to you under the terms of version 3 of the * GNU Affero General Public License. This program is distributed WITHOUT * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT, * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details. * */ package org.rstudio.core.client; import com.google.gwt.user.client.Command; import com.google.inject.Inject; import com.google.inject.Provider; /** * AsyncShim makes it easy to sequester a class behind GWT.runAsync() * and cause it to be lazy-loaded, while still making it convenient to * call (some types of) methods on it from other parts of the code that * are not in the same fragment. * * For each type that needs to be sequestered, create an *abstract* * AsyncShim subclass with the TTarget type parameter set to the * sequestered type. (The AsyncShim subclass must be at package-level * or public visibility.) * * In your AsyncShim subclass, you'll want to override onDelayLoadSuccess * and onDelayLoadFailure to do whatever your app needs. onDelayLoadSuccess * will be called only once, when the instance of TTarget is first created. * * You can also add any other fields, methods, etc. to your subclass that * are necessary to give onDelayLoadSuccess and/or onDelayLoadFailure any * contextual state they need. * * As mentioned, AsyncShim can make some kinds of methods easy to call from * other fragments. Specifically, any method that returns void and * semantically makes sense to run asynchronously, can be easily passed * through AsyncShim. Just stub out an abstract version of the method on * your AsyncShim subclass, and deferred binding will take care of wiring * everything up. Or, you can have your AsyncShim subclass "implement" an * interface but not actually provide implementations--if these methods * return void, they will also be automatically wired up. * * @param <TTarget> The type to be sequestered */ public abstract class AsyncShim<TTarget> { /** * [DON'T override this, it will be overridden by the code generator] * * This method must be called before the target object is needed. */ @Inject public void initialize(Provider<TTarget> provider) { } public final void forceLoad(boolean downloadCodeOnly) { forceLoad(downloadCodeOnly, null); } /** * [DON'T override this, it will be overridden by the code generator] * * Call this to force the code to be downloaded, and optionally, for * the provider to be invoked * * @param downloadCodeOnly If true, the code will be downloaded but * the provider will not be invoked * @param continuation A command to invoke after the load is complete * (regardless of whether the effort was successful or not). Can be * null. */ public void forceLoad(boolean downloadCodeOnly, Command continuation) { } /** * You can override this to do something asynchronous between when the * code loads and when the instance is created. * @param continuation You MUST call this (eventually) to allow execution * to proceed */ protected void preInstantiationHook(Command continuation) { continuation.execute(); } /** * You can override this to do something with the delayed type once the * code loads and the instance is created. * @param obj */ protected void onDelayLoadSuccess(TTarget obj) { } /** * You can (should!) override this to deal with failure cases. * @param reason */ protected void onDelayLoadFailure(Throwable reason) { } }