@Override
public void renderProvider(final InjectableInstance injectableInstance) {
if (isRendered()) return;
final InjectionContext injectContext = injectableInstance.getInjectionContext();
final IOCProcessingContext ctx = injectContext.getProcessingContext();
/*
get a parameterized version of the BeanProvider class, parameterized with the type of
bean it produces.
*/
final MetaClass beanProviderClassRef = parameterizedAs(AsyncBeanProvider.class, typeParametersOf(type));
final MetaClass creationalCallbackClassRef = parameterizedAs(CreationalCallback.class, typeParametersOf(type));
/*
begin building the creational callback, implement the "getInstance" method from the interface
and assign its BlockBuilder to a callbackBuilder so we can work with it.
*/
final BlockBuilder<AnonymousClassStructureBuilder> callbackBuilder
= newInstanceOf(beanProviderClassRef).extend()
.publicOverridesMethod("getInstance", Parameter.of(creationalCallbackClassRef, "callback", true),
Parameter.of(AsyncCreationalContext.class, "context", true));
final boolean loadAsync = type.isAnnotationPresent(LoadAsync.class);
final BlockBuilder<AnonymousClassStructureBuilder> targetBlock;
if (loadAsync) {
final BlockBuilder<AnonymousClassStructureBuilder> asyncBuilder = ObjectBuilder.newInstanceOf(RunAsyncCallback.class).extend()
.publicOverridesMethod("onFailure", Parameter.of(Throwable.class, "throwable"))
.append(Stmt.throw_(RuntimeException.class, "failed to run asynchronously", Refs.get("throwable")))
.finish()
.publicOverridesMethod("onSuccess");
targetBlock = asyncBuilder;
}
else {
targetBlock = callbackBuilder;
}
/* push the method block builder onto the stack, so injection tasks are rendered appropriately. */
ctx.pushBlockBuilder(targetBlock);
targetBlock.append(
Stmt.create().declareFinalVariable("beanRef", BeanRef.class,
loadVariable("context").invoke("getBeanReference", load(type),
load(qualifyingMetadata.getQualifiers()))
));
targetBlock.append(
Stmt.create().declareFinalVariable("async", AsyncBeanContext.class,
Stmt.create().newObject(AsyncBeanContext.class))
);
/* get a new unique variable for the creational callback */
creationalCallbackVarName = InjectUtil.getNewInjectorName().concat("_")
.concat(type.getName()).concat("_creational");
/* get the construction strategy and execute it to wire the bean */
AsyncInjectUtil.getConstructionStrategy(this, injectContext).generateConstructor(new ConstructionStatusCallback() {
@Override
public void beanConstructed(final ConstructionType constructionType) {
/* the bean has been constructed, so get a reference to the BeanRef and set it to the 'beanRef' variable. */
injectContext.getProcessingContext().append(
loadVariable("context").invoke("addBean", Refs.get("beanRef"), Refs.get(instanceVarName))
);
/* add the bean to SimpleCreationalContext */
final ObjectBuilder objectBuilder = Stmt.create().newObject(Runnable.class);
final BlockBuilder<AnonymousClassStructureBuilder> blockBuilder = objectBuilder
.extend().publicOverridesMethod("run");
final BlockBuilderUpdater updater
= new BlockBuilderUpdater(injectContext, AsyncTypeInjector.this, constructionType, targetBlock, blockBuilder);
setAttribute("BlockBuilderUpdater", updater);
final Statement beanRef = updater.run();
injectContext.addBeanReference(type, beanRef);
addStatementToEndOfInjector(Stmt.loadVariable("async").invoke("runOnFinish", blockBuilder.finish().finish()));
/* mark this injector as injected so we don't go into a loop if there is a cycle. */
setCreated(true);
}
});
/*
return the instance of the bean from the creational callback.
*/
targetBlock.appendAll(getAddToEndStatements());
targetBlock._(Stmt.loadVariable("async").invoke("finish"));
/* pop the block builder of the stack now that we're done wiring. */
ctx.popBlockBuilder();
if (loadAsync) {
final ObjectBuilder objectBuilder = targetBlock.finish().finish();
final String frameworkOrSystemProperty
= EnvUtil.getEnvironmentConfig().getFrameworkOrSystemProperty("errai.ioc.testing.simulated_loadasync_latency");
if (Boolean.parseBoolean(frameworkOrSystemProperty)) {
callbackBuilder.append(Stmt.invokeStatic(FakeGWT.class, "runAsync", objectBuilder));
}
else {
callbackBuilder.append(Stmt.invokeStatic(GWT.class, "runAsync", objectBuilder));
}
}
/*
declare a final variable for the BeanProvider and initialize it with the anonymous class we just
built.
*/
ctx.getBootstrapBuilder().privateField(creationalCallbackVarName, beanProviderClassRef).modifiers(Modifier.Final)
.initializesWith(callbackBuilder.finish().finish()).finish();
if (isSingleton()) {
registerWithBeanManager(injectContext, Stmt.load(true));
}