/* * Copyright (C) 2012 Red Hat, Inc. and/or its affiliates. * * 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.config.rebind; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.jboss.errai.codegen.meta.MetaClass; import org.jboss.errai.codegen.meta.MetaClassCache; import org.jboss.errai.codegen.meta.MetaClassFactory; import org.jboss.errai.codegen.meta.impl.gwt.GWTClass; import org.jboss.errai.codegen.meta.impl.java.JavaReflectionClass; import org.jboss.errai.common.metadata.RebindUtils; import org.jboss.errai.common.rebind.CacheStore; import org.jboss.errai.common.rebind.CacheUtil; import com.google.gwt.core.ext.GeneratorContext; import com.google.gwt.core.ext.TreeLogger; import com.google.gwt.core.ext.typeinfo.JClassType; import com.google.gwt.core.ext.typeinfo.TypeOracle; /** * @author Mike Brock */ public abstract class MetaClassBridgeUtil { private MetaClassBridgeUtil() {} /** * Erases the {@link org.jboss.errai.codegen.meta.MetaClassFactory} cache, then populates it with types * discovered via GWT's TypeOracle. The reason for the initial flush of the * MetaClassFactory is to support hot redeploy in Dev Mode. The reason for doing * this operation at all is so that the overridden class definitions * (super-source classes) are used in preference to the Java reflection based * class definitions. * * @param context * The GeneratorContext supplied by the GWT compiler. Not null. * @param logger * The TreeLogger supplied by the GWT compiler. Not null. */ public synchronized static void populateMetaClassFactoryFromTypeOracle(final GeneratorContext context, final TreeLogger logger) { final GWTTypeOracleCacheStore tOCache = CacheUtil.getCache(GWTTypeOracleCacheStore.class); // if we're in production mode -- it means we're compiling, and we do not need to accommodate dynamically // changing classes. Therefore, do a NOOP after the first successful call. if (context.equals(tOCache.populatedFrom)) { return; } final TypeOracle typeOracle = context.getTypeOracle(); final MetaClassCache cache = MetaClassFactory.getMetaClassCache(); if (typeOracle != null) { final Map<String, MetaClass> classesToPush = new HashMap<String, MetaClass>(typeOracle.getTypes().length); final Set<String> translatable = new HashSet<String>(RebindUtils.findTranslatablePackages(context)); // Need to remove these or else we get issues from annotations and loading // of emulated Object and Class instead of real ones. translatable.remove("java.lang"); translatable.remove("java.lang.annotation"); final Set<String> reloadable = RebindUtils.getReloadablePackageNames(context); for (final JClassType type : typeOracle.getTypes()) { if (!translatable.contains(type.getPackage().getName())) { continue; } if (type.isAnnotation() != null || type.getQualifiedSourceName().equals("java.lang.annotation.Annotation")) { if (!MetaClassFactory.canLoadClass(type.getQualifiedBinaryName())) { throw new RuntimeException("a new annotation has been introduced (" + type.getQualifiedSourceName() + "); " + "you cannot currently introduce new annotations in devmode. Please restart."); } final MetaClass clazz = JavaReflectionClass .newUncachedInstance(MetaClassFactory.loadClass(type.getQualifiedBinaryName())); if (isReloadable(clazz, reloadable)) { classesToPush.put(clazz.getFullyQualifiedName(), clazz); } else if (!cache.isKnownType(clazz.getFullyQualifiedName())) { cache.pushCache(clazz); } } else { logger.log(TreeLogger.Type.DEBUG, "Caching translatable type " + type.getQualifiedSourceName()); final MetaClass clazz = GWTClass.newUncachedInstance(typeOracle, type); if (isReloadable(clazz, reloadable)) { classesToPush.put(clazz.getFullyQualifiedName(), clazz); } else if (!cache.isKnownType(clazz.getFullyQualifiedName())) { cache.pushCache(clazz); } } } cache.updateCache(classesToPush); } tOCache.populatedFrom = context; CacheUtil.getCache(EnvUtil.EnvironmentConfigCache.class).clear(); } private static boolean isReloadable(final MetaClass clazz, final Set<String> reloadablePacakges) { for (final String packageName : reloadablePacakges) { if (clazz.getPackageName().startsWith(packageName)) return true; } return false; } public static class GWTTypeOracleCacheStore implements CacheStore { volatile GeneratorContext populatedFrom; @Override public void clear() { populatedFrom = null; } } }