Errai and Polymer
aanderson1776 Aug 20, 2016 5:13 PMIn case anyone is interested I was able to utilize the Vaadin Polymer GWT 2.8 JSInterop classes with Errai templates. Additionally I was able to use the Vaadin Polymer gwt-api-generator to generate JSInterop classes for Polymer app-layout elements. At some point when I have time I would like to provide a public example of the-end-to end process but until then here is an overall description of the process:
1) install NPM, Bower, and the Vaadin API generator
sudo apt-get install npm
sudo npm install -g bower
sudo npm install -g vaadin/gwt-api-generator
2) In an empy directory generate the JSInterop stubs. Skip this step if you are using only standard Polymer libraries
gwt-api-generator --package "PolymerLabs/app-layout `realpath ../web/bower.json`" --groupId=com.mycompany.polymer --artifactId=polymer-gwt --version 0.0.1 --pom
Note that the contents of of ../web is just a directory where I place my own Polymer elements that I want to generate bindings for. The bower.json looks like
{
"name": "mycompany-web",
"version": "0.0.1",
"description": "My Company Common Web Components",
"private": true
}
run mvn clean install to compile the JSInerop classes
3) In the main maven project add the Errai dependencies plus the Vaadin ones
<dependency>
<groupId>com.vaadin.polymer</groupId>
<artifactId>vaadin-gwt-polymer-elements</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.mycompany.polymer</groupId>
<artifactId>polymer-gwt</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
Since GWT/Errai asset generation is performed at compile time one can set all the GWT/Errai dependencies to the maven provided scope so they are not added to the WAR archive. Obviously if one is using server side features such as WebSockets, CDI, etc those libraries will need to be in the compile scope.
4) Add an empty Errai.properies file in src/main/resources to enable Errai extensions and then add this class to the project:
package mycompany.rebind;
import static org.jboss.errai.codegen.util.Stmt.declareFinalVariable;
import static org.jboss.errai.codegen.util.Stmt.invokeStatic;
import static org.jboss.errai.codegen.util.Stmt.loadLiteral;
import static org.jboss.errai.codegen.util.Stmt.loadVariable;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.logging.Logger;
import javax.enterprise.context.Dependent;
import org.jboss.errai.codegen.Statement;
import org.jboss.errai.codegen.builder.ClassStructureBuilder;
import org.jboss.errai.codegen.meta.MetaClass;
import org.jboss.errai.codegen.meta.MetaClassFactory;
import org.jboss.errai.common.server.api.ErraiBootstrapFailure;
import org.jboss.errai.ioc.client.api.IOCExtension;
import org.jboss.errai.ioc.rebind.ioc.bootstrapper.AbstractBodyGenerator;
import org.jboss.errai.ioc.rebind.ioc.bootstrapper.FactoryBodyGenerator;
import org.jboss.errai.ioc.rebind.ioc.bootstrapper.IOCProcessingContext;
import org.jboss.errai.ioc.rebind.ioc.extension.IOCExtensionConfigurator;
import org.jboss.errai.ioc.rebind.ioc.graph.api.CustomFactoryInjectable;
import org.jboss.errai.ioc.rebind.ioc.graph.api.DependencyGraph;
import org.jboss.errai.ioc.rebind.ioc.graph.api.DependencyGraphBuilder.InjectableType;
import org.jboss.errai.ioc.rebind.ioc.graph.api.Injectable;
import org.jboss.errai.ioc.rebind.ioc.graph.api.InjectionSite;
import org.jboss.errai.ioc.rebind.ioc.graph.impl.DefaultCustomFactoryInjectable;
import org.jboss.errai.ioc.rebind.ioc.graph.impl.FactoryNameGenerator;
import org.jboss.errai.ioc.rebind.ioc.graph.impl.InjectableHandle;
import org.jboss.errai.ioc.rebind.ioc.injector.api.ExtensionTypeCallback;
import org.jboss.errai.ioc.rebind.ioc.injector.api.InjectableProvider;
import org.jboss.errai.ioc.rebind.ioc.injector.api.InjectionContext;
import org.jboss.errai.ioc.rebind.ioc.injector.api.WiringElementType;
import org.jboss.errai.ui.shared.TemplateUtil;
import com.google.gwt.dom.client.Document;
import com.vaadin.polymer.elemental.HTMLElement;
import jsinterop.annotations.JsType;
@IOCExtension
public class VaadinPolymerIOCExtension implements IOCExtensionConfigurator {
Logger logger = Logger.getLogger(VaadinPolymerIOCExtension.class.getName());
@Override
public void configure(IOCProcessingContext context, InjectionContext injectionContext) {
}
@Override
public void afterInitialization(IOCProcessingContext context, InjectionContext injectionContext) {
final MetaClass polymerClazz = MetaClassFactory.get(HTMLElement.class);
injectionContext.registerExtensionTypeCallback(new ExtensionTypeCallback() {
@Override
public void callback(final MetaClass type) {
if (type.isAssignableTo(polymerClazz)) {
// org.jboss.errai.codegen.meta.impl.gwt.GWTClass MetaClass
// implementation filters by class vs interface so convert
// to Java reflection type to iterate over fields
try {
for (Field field : type.asClass().getDeclaredFields()) {
if ("TAG".equals(field.getName())) {
String tagName = (String) field.get(null);
final JsType jsTypeAnno;
if ((jsTypeAnno = type.getAnnotation(JsType.class)) == null || !jsTypeAnno.isNative()) {
throw new RuntimeException(HTMLElement.class.getSimpleName() + " is only valid on native " + JsType.class.getSimpleName() + "s.");
}
addProviderForVaadinPolymerType(injectionContext, type, tagName);
}
}
} catch (IllegalAccessException ie) {
throw new ErraiBootstrapFailure(ie);
}
}
}
});
}
private void addProviderForVaadinPolymerType(final InjectionContext injectionContext, final MetaClass polymerType, String tagName) {
if (polymerType.isPublic()) {
final InjectableHandle handle = new InjectableHandle(polymerType, injectionContext.getQualifierFactory().forDefault());
injectionContext.registerExactTypeInjectableProvider(handle, new InjectableProvider() {
private CustomFactoryInjectable provided;
@Override
public CustomFactoryInjectable getInjectable(final InjectionSite injectionSite, final FactoryNameGenerator nameGenerator) {
if (provided == null) {
final FactoryBodyGenerator generator = new AbstractBodyGenerator() {
@Override
protected List<Statement> generateCreateInstanceStatements(final ClassStructureBuilder<?> bodyBlockBuilder, final Injectable injectable, final DependencyGraph graph, final InjectionContext injectionContext) {
final List<Statement> stmts = new ArrayList<Statement>();
final String elementVar = "element";
stmts.add(declareFinalVariable(elementVar, com.google.gwt.dom.client.Element.class, invokeStatic(Document.class, "get").invoke("createElement", loadLiteral(tagName))));
final String retValVar = "retVal";
stmts.add(declareFinalVariable(retValVar, polymerType, invokeStatic(TemplateUtil.class, "nativeCast", loadVariable(elementVar))));
stmts.add(loadVariable(retValVar).returnValue());
return stmts;
}
};
provided = new DefaultCustomFactoryInjectable(handle.getType(), handle.getQualifier(), nameGenerator.generateFor(handle.getType(), handle.getQualifier(), InjectableType.ExtensionProvided), Dependent.class, Collections.singletonList(WiringElementType.DependentBean), generator);
}
return provided;
}
});
}
}
}
After this one may inject the Vaadin generated JSInterop classes into Errai templates just as any other template composite.