Java tutorial
/* * Copyright 2010 JBoss, a divison Red Hat, 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 org.jboss.errai.bus.rebind; import com.google.gwt.core.ext.GeneratorContext; import com.google.gwt.core.ext.TreeLogger; import com.google.gwt.core.ext.typeinfo.*; import com.google.gwt.user.rebind.SourceWriter; import org.jboss.errai.bus.server.ErraiBootstrapFailure; import org.jboss.errai.bus.server.annotations.ExposeEntity; import org.jboss.errai.bus.server.annotations.Remote; import org.jboss.errai.bus.server.service.ErraiServiceConfigurator; import org.jboss.errai.bus.server.service.metadata.MetaDataScanner; import org.mvel2.templates.CompiledTemplate; import org.mvel2.util.Make; import org.mvel2.util.ParseTools; import org.mvel2.util.ReflectionUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.InputStream; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Properties; import static org.mvel2.templates.TemplateCompiler.compileTemplate; import static org.mvel2.templates.TemplateRuntime.execute; /** * @author Mike Brock * @author Heiko Braun */ public class BusClientConfigGenerator implements ExtensionGenerator { private Logger log = LoggerFactory.getLogger(BusClientConfigGenerator.class); private CompiledTemplate demarshallerGenerator; private CompiledTemplate marshallerGenerator; private CompiledTemplate rpcProxyGenerator; public BusClientConfigGenerator() { InputStream istream = this.getClass().getResourceAsStream("DemarshallerGenerator.mv"); demarshallerGenerator = compileTemplate(istream, null); istream = this.getClass().getResourceAsStream("MarshallerGenerator.mv"); marshallerGenerator = compileTemplate(istream, null); istream = this.getClass().getResourceAsStream("RPCProxyGenerator.mv"); rpcProxyGenerator = compileTemplate(istream, null); } public void generate(GeneratorContext context, TreeLogger logger, SourceWriter writer, MetaDataScanner scanner, final TypeOracle oracle) { for (Class<?> entity : scanner.getTypesAnnotatedWith(ExposeEntity.class)) { generateMarshaller(loadType(oracle, entity), logger, writer); } for (Class<?> remote : scanner.getTypesAnnotatedWith(Remote.class)) { JClassType type = loadType(oracle, remote); try { writer.print((String) execute(rpcProxyGenerator, Make.Map.<String, Object>$()._("implementationClassName", type.getName() + "Impl") ._("interfaceClass", Class.forName(type.getQualifiedSourceName()))._())); } catch (Throwable t) { throw new ErraiBootstrapFailure(t); } } Properties props = scanner.getProperties("ErraiApp.properties"); if (props != null) { logger.log(TreeLogger.Type.INFO, "Checking ErraiApp.properties for configured types ..."); for (Object o : props.keySet()) { String key = (String) o; /** * Types configuration */ if (ErraiServiceConfigurator.CONFIG_ERRAI_SERIALIZABLE_TYPE.equals(key)) { for (String s : props.getProperty(key).split(" ")) { try { generateMarshaller(oracle.getType(s.trim()), logger, writer); } catch (Exception e) { e.printStackTrace(); throw new ErraiBootstrapFailure(e); } } } /** * Entity configuration */ else if (ErraiServiceConfigurator.CONFIG_ERRAI_SERIALIZABLE_TYPE.equals(key)) { for (String s : props.getProperty(key).split(" ")) { try { generateMarshaller(oracle.getType(s.trim()), logger, writer); } catch (Exception e) { e.printStackTrace(); throw new ErraiBootstrapFailure(e); } } } } } else { // props not found log.warn("No modules found ot load. Unable to find ErraiApp.properties in the classpath"); } } private JClassType loadType(TypeOracle oracle, Class<?> entity) { try { return oracle.getType(entity.getCanonicalName()); } catch (NotFoundException e) { throw new RuntimeException("Failed to load type " + entity.getName(), e); } } private void generateMarshaller(JClassType visit, TreeLogger logger, SourceWriter writer) { Boolean enumType = visit.isEnum() != null; Map<String, Class> types = new HashMap<String, Class>(); Map<String, ValueExtractor> getters = new HashMap<String, ValueExtractor>(); Map<String, ValueBinder> setters = new HashMap<String, ValueBinder>(); Map<Class, Integer> arrayConverters = new HashMap<Class, Integer>(); try { JClassType scan = visit; do { for (JField f : scan.getFields()) { if (f.isTransient() || f.isStatic() || f.isEnumConstant() != null) continue; JClassType type = f.getType().isClassOrInterface(); JMethod getterMeth; JMethod setterMeth; if (type == null) { JPrimitiveType pType = f.getType().isPrimitive(); Class c; if (pType == null) { JArrayType aType = f.getType().isArray(); if (aType == null) continue; String name = aType.getQualifiedBinaryName(); int depth = 0; for (int i = 0; i < name.length(); i++) { if (name.charAt(i) == '[') depth++; else break; } types.put(f.getName(), c = Class.forName(name.substring(0, depth) + getInternalRep(aType.getQualifiedBinaryName().substring(depth)))); arrayConverters.put(c, depth); } else { types.put(f.getName(), c = ParseTools .unboxPrimitive(Class.forName(pType.getQualifiedBoxedSourceName()))); } } else { types.put(f.getName(), Class.forName(type.getQualifiedBinaryName())); } getterMeth = getAccessorMethod(visit, f); setterMeth = getSetterMethod(visit, f); if (getterMeth == null) { if (f.isPublic()) { getters.put(f.getName(), new ValueExtractor(f)); } else if (visit == scan) { throw new GenerationException("could not find a read accessor in class: " + visit.getQualifiedSourceName() + "; for field: " + f.getName() + "; should declare an accessor: " + ReflectionUtil.getGetter(f.getName())); } } else { getters.put(f.getName(), new ValueExtractor(getterMeth)); } if (setterMeth == null) { if (f.isPublic()) { setters.put(f.getName(), new ValueBinder(f)); } else if (visit == scan) { throw new GenerationException("could not find a write accessor in class: " + visit.getQualifiedSourceName() + "; for field: " + f.getName() + "; should declare an accessor: " + ReflectionUtil.getSetter(f.getName())); } } else { setters.put(f.getName(), new ValueBinder(setterMeth)); } } } while ((scan = scan.getSuperclass()) != null && !scan.getQualifiedSourceName().equals("java.lang.Object")); } catch (ClassNotFoundException e) { e.printStackTrace(); throw new RuntimeException(e); } try { if (!enumType) visit.getConstructor(new JClassType[0]); } catch (NotFoundException e) { String errorMsg = "Type marked for serialization does not expose a default constructor: " + visit.getQualifiedSourceName(); logger.log(TreeLogger.Type.ERROR, errorMsg, e); throw new GenerationException(errorMsg, e); } Map<String, Object> templateVars = Make.Map.<String, Object>$() ._("className", visit.getQualifiedSourceName()) ._("canonicalClassName", visit.getQualifiedBinaryName())._("fields", types.keySet()) ._("targetTypes", types)._("getters", getters)._("setters", setters) ._("arrayConverters", arrayConverters)._("enumType", enumType)._(); String genStr; writer.print(genStr = (String) execute(demarshallerGenerator, templateVars)); log.debug("generated demarshaller: \n" + genStr); logger.log(TreeLogger.Type.INFO, genStr); writer.print(genStr = (String) execute(marshallerGenerator, templateVars)); log.debug("generated marshaller: \n" + genStr); logger.log(TreeLogger.Type.INFO, genStr); logger.log(TreeLogger.Type.INFO, "Generated marshaller/demarshaller for: " + visit.getName()); } public static class ValueExtractor { private boolean accessor; private String name; public ValueExtractor(JMethod m) { accessor = true; name = m.getName(); } public ValueExtractor(JField f) { accessor = false; name = f.getName(); } @Override public String toString() { return accessor ? name + "()" : name; } } public static class ValueBinder { private boolean accessor; private String name; public ValueBinder(JMethod m) { accessor = true; name = m.getName(); } public ValueBinder(JField f) { accessor = false; name = f.getName(); } public String bindValue(String expr) { return accessor ? name + "(" + expr + ")" : "name = " + expr; } } private static JMethod getAccessorMethod(JClassType clazz, JField field) { JMethod m = null; if (field.getType().getQualifiedSourceName().equals("boolean")) m = _findGetterMethod(clazz, ReflectionUtil.getIsGetter(field.getName())); if (m == null) m = _findGetterMethod(clazz, ReflectionUtil.getGetter(field.getName())); if (m == null) m = _findGetterMethod(clazz, "get" + field.getName()); return m; } private static JMethod getSetterMethod(JClassType clazz, JField field) { JMethod m = null; m = _findSetterMethod(clazz, field.getType(), ReflectionUtil.getSetter(field.getName())); if (m == null) m = _findSetterMethod(clazz, field.getType(), "set" + field.getName()); return m; } private static JMethod _findGetterMethod(JClassType clazz, String methName) { JClassType scan = clazz; do { try { return scan.getMethod(methName, new JType[0]); } catch (NotFoundException e) { // } } while ((scan = scan.getSuperclass()) != null && !scan.getQualifiedSourceName().equals("java.lang.Object")); return null; } private static JMethod _findSetterMethod(JClassType clazz, JType field, String methName) { JClassType scan = clazz; do { try { return scan.getMethod(methName, new JType[] { field }); } catch (NotFoundException e) { // } } while ((scan = scan.getSuperclass()) != null && !scan.getQualifiedSourceName().equals("java.lang.Object")); return null; } private String getInternalRep(String c) { if ("char".equals(c)) { return "C"; } else if ("byte".equals(c)) { return "B"; } else if ("double".equals(c)) { return "D"; } else if ("float".equals(c)) { return "F"; } else if ("int".equals(c)) { return "I"; } else if ("long".equals(c)) { return "J"; } else if ("short".equals(c)) { return "S"; } else if ("boolean".equals(c)) { return "Z"; } return "L" + c + ";"; } }