org.jboss.errai.bus.rebind.BusClientConfigGenerator.java Source code

Java tutorial

Introduction

Here is the source code for org.jboss.errai.bus.rebind.BusClientConfigGenerator.java

Source

/*
 * 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 + ";";
    }

}