/* * Copyright (C) 2015 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.util; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.atomic.AtomicLong; import java.util.regex.Pattern; import org.jboss.errai.codegen.meta.MetaClass; import org.jboss.errai.codegen.meta.MetaClassFactory; import org.jboss.errai.codegen.meta.MetaField; import org.jboss.errai.codegen.meta.MetaMethod; import org.jboss.errai.codegen.meta.MetaParameter; import org.jboss.errai.codegen.meta.impl.java.JavaReflectionField; import org.jboss.errai.codegen.meta.impl.java.JavaReflectionMethod; import org.jboss.errai.common.metadata.RebindUtils; import org.jboss.errai.common.metadata.ScannerSingleton; import org.jboss.errai.common.rebind.CacheStore; import org.jboss.errai.common.rebind.CacheUtil; import org.mvel2.util.NullType; import com.google.gwt.core.ext.GeneratorContext; /** * @author Mike Brock * @author Christian Sadilek <csadilek@redhat.com> */ public final class ClassScanner { public static class CacheHolder implements CacheStore { final Map<MetaClass, Collection<MetaClass>> subtypesCache = new ConcurrentHashMap<MetaClass, Collection<MetaClass>>(); final Collection<MetaClass> reloadableClasses = new CopyOnWriteArrayList<MetaClass>(); final Set<String> reloadableClassNames = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>()); final Set<String> reloadablePackages = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>()); @Override public void clear() { subtypesCache.clear(); reloadableClasses.clear(); reloadableClassNames.clear(); reloadablePackages.clear(); } } final static CacheHolder cache = CacheUtil.getCache(CacheHolder.class); private static final ThreadLocal<Boolean> reflectionScanLocal = new ThreadLocal<Boolean>() { @Override protected Boolean initialValue() { return Boolean.FALSE; } }; private static AtomicLong totalClassScanTime = new AtomicLong(0); private ClassScanner() { } public static Collection<MetaParameter> getParametersAnnotatedWith(final Class<? extends Annotation> annotation, final Set<String> packages, final GeneratorContext genCtx) { final Collection<MetaParameter> result = new HashSet<MetaParameter>(); if (genCtx != null) { for (final MetaClass metaClass : getAllReloadableCachedClasses(genCtx)) { for (final MetaMethod method : metaClass.getDeclaredMethods()) { for (final MetaParameter parameter : method.getParameters()) { if (parameter.isAnnotationPresent(annotation)) { result.add(parameter); } } } } try { for (final Method m : ScannerSingleton.getOrCreateInstance().getMethodsWithAnyParamAnnotated(annotation)) { Class<?> clazz = m.getDeclaringClass(); if (!isReloadable(clazz) && MetaClassFactory.isKnownType(clazz.getName())) { MetaMethod mm = new JavaReflectionMethod(m); for (final MetaParameter parameter : mm.getParameters()) { if (parameter.isAnnotationPresent(annotation)) { result.add(parameter); } } } } } catch (Exception ignored) { } } else { for (final MetaClass metaClass : MetaClassFactory.getAllCachedClasses()) { for (final MetaMethod method : metaClass.getDeclaredMethods()) { for (final MetaParameter parameter : method.getParameters()) { if (parameter.isAnnotationPresent(annotation)) { result.add(parameter); } } } } } filterResultsParameter(result, packages, null); return Collections.unmodifiableCollection(result); } public static Collection<MetaParameter> getParametersAnnotatedWith(final Class<? extends Annotation> annotation, final GeneratorContext genCtx) { return getParametersAnnotatedWith(annotation, null, genCtx); } public static Collection<MetaClass> getTypesAnnotatedWith(final Class<? extends Annotation> annotation, final Set<String> packages, final String excludeRegEx, final GeneratorContext genCtx, boolean reflections) { final Collection<MetaClass> result = new HashSet<MetaClass>(); if (genCtx != null) { for (final MetaClass metaClass : getAllReloadableCachedClasses(genCtx)) { if (metaClass.isAnnotationPresent(annotation)) { result.add(metaClass); } } try { for (final Class<?> cls : ScannerSingleton.getOrCreateInstance().getTypesAnnotatedWith(annotation)) { if (!isReloadable(cls) && MetaClassFactory.isKnownType(cls.getName())) { result.add(MetaClassFactory.get(cls)); } } } catch (Exception ignored) { } } else { for (final MetaClass metaClass : MetaClassFactory.getAllCachedClasses()) { if (metaClass.isAnnotationPresent(annotation)) { result.add(metaClass); } } try { if (reflections || reflectionScanLocal.get()) { for (final Class<?> cls : ScannerSingleton.getOrCreateInstance().getTypesAnnotatedWith(annotation)) { final MetaClass e = MetaClassFactory.get(cls); result.add(e); } } } catch (Exception ignored) { } } filterResultsClass(result, packages, excludeRegEx); return Collections.unmodifiableCollection(result); } public static Collection<MetaClass> getTypesAnnotatedWith(final Class<? extends Annotation> annotation) { return getTypesAnnotatedWith(annotation, null, null, null, false); } public static Collection<MetaClass> getTypesAnnotatedWith(final Class<? extends Annotation> annotation, boolean reflections) { return getTypesAnnotatedWith(annotation, null, null, null, reflections); } public static Collection<MetaClass> getTypesAnnotatedWith(final Class<? extends Annotation> annotation, final GeneratorContext genCtx) { return getTypesAnnotatedWith(annotation, null, null, genCtx, true); } public static Collection<MetaClass> getTypesAnnotatedWith(final Class<? extends Annotation> annotation, final Set<String> packages, final GeneratorContext genCtx) { return getTypesAnnotatedWith(annotation, packages, null, genCtx, true); } public static Collection<MetaClass> getTypesAnnotatedWithExcluding(final Class<? extends Annotation> annotation, final String excludeRegex, final GeneratorContext genCtx) { return getTypesAnnotatedWith(annotation, null, excludeRegex, genCtx, true); } public static Collection<MetaMethod> getMethodsAnnotatedWith(final Class<? extends Annotation> annotation, final Set<String> packages, final GeneratorContext genCtx) { final Collection<MetaMethod> result = new HashSet<MetaMethod>(50); if (genCtx != null) { for (final MetaClass metaClass : getAllReloadableCachedClasses(genCtx)) { for (final MetaMethod metaMethod : metaClass.getDeclaredMethods()) { if (metaMethod.isAnnotationPresent(annotation)) { result.add(metaMethod); } } } try { for (final Method m : ScannerSingleton.getOrCreateInstance().getMethodsAnnotatedWith(annotation)) { Class<?> clazz = m.getDeclaringClass(); if (!isReloadable(clazz) && MetaClassFactory.isKnownType(clazz.getName())) { final MetaClass metaClass = MetaClassFactory.get(clazz); result.add(new JavaReflectionMethod(metaClass, m)); } } } catch (Exception ignored) { } } else { for (final MetaClass metaClass : MetaClassFactory.getAllCachedClasses()) { for (final MetaMethod metaMethod : metaClass.getDeclaredMethods()) { if (metaMethod.isAnnotationPresent(annotation)) { result.add(metaMethod); } } } } filterResultsMethod(result, packages, null); return Collections.unmodifiableCollection(result); } public static Collection<MetaField> getFieldsAnnotatedWith(final Class<? extends Annotation> annotation, final Set<String> packages, final GeneratorContext genCtx) { final Collection<MetaField> result = new HashSet<MetaField>(50); if (genCtx != null) { for (final MetaClass metaClass : getAllReloadableCachedClasses(genCtx)) { for (final MetaField metaField : metaClass.getDeclaredFields()) { if (metaField.isAnnotationPresent(annotation)) { result.add(metaField); } } } try { for (final Field f : ScannerSingleton.getOrCreateInstance().getFieldsAnnotatedWith(annotation)) { Class<?> clazz = f.getDeclaringClass(); if (!isReloadable(clazz) && MetaClassFactory.isKnownType(clazz.getName())) { result.add(new JavaReflectionField(f)); } } } catch (Exception ignored) { } } else { for (final MetaClass metaClass : MetaClassFactory.getAllCachedClasses()) { for (final MetaField metaField : metaClass.getDeclaredFields()) { if (metaField.isAnnotationPresent(annotation)) { result.add(metaField); } } } } filterResultsField(result, packages, null); return Collections.unmodifiableCollection(result); } public static Collection<MetaClass> getSubTypesOf(final MetaClass metaClass, final GeneratorContext genCtx) { final MetaClass root = metaClass.getErased(); if (cache.subtypesCache.containsKey(root)) { return cache.subtypesCache.get(root); } final Set<MetaClass> result = Collections.newSetFromMap(new ConcurrentHashMap<MetaClass, Boolean>()); if (!Boolean.getBoolean("org.jboss.errai.skip.reloadable.subtypes")) { for (final MetaClass mc : getAllReloadableCachedClasses(genCtx)) { if (!NullType.class.getName().equals(mc.getFullyQualifiedName()) && !root.getFullyQualifiedName().equals(mc.getFullyQualifiedName()) && root.isAssignableFrom(mc)) { result.add(mc.getErased()); } } } final Class<?> cls = root.asClass(); if (cls != null && !cls.equals(NullType.class)) { for (final Class<?> c : ScannerSingleton.getOrCreateInstance().getSubTypesOf(cls)) { if (!c.isAnonymousClass() && !c.isSynthetic()) { result.add(MetaClassFactory.get(c)); } } } cache.subtypesCache.put(root, result); return result; } private static Collection<MetaClass> getAllReloadableCachedClasses(final GeneratorContext context) { if (cache.reloadablePackages.isEmpty()) { cache.reloadablePackages.addAll(RebindUtils.getReloadablePackageNames(context)); } if (!cache.reloadableClasses.isEmpty()) { return cache.reloadableClasses; } final Collection<MetaClass> classes = new ArrayList<MetaClass>(); final Collection<String> classNames = new ArrayList<String>(); for (MetaClass clazz : MetaClassFactory.getAllCachedClasses()) { final String fqcn = clazz.getFullyQualifiedName(); final String pkg = clazz.getPackageName(); if (pkg !=null && cache.reloadablePackages.contains(pkg)) { classes.add(clazz); classNames.add(fqcn); } else { for (String reloadablePackage : cache.reloadablePackages) { if (fqcn.startsWith(reloadablePackage)) { classes.add(clazz); classNames.add(fqcn); if (pkg != null) { cache.reloadablePackages.add(pkg); } break; } } } } cache.reloadableClasses.addAll(classes); cache.reloadableClassNames.addAll(classNames); return classes; } private static boolean isReloadable(Class<?> clazz) { return cache.reloadableClassNames.contains(clazz.getName()); } private static void filterResultsClass(final Collection<MetaClass> result, final Set<String> packages, final String excludeRegEx) { final Pattern excludePattern; if (excludeRegEx != null) { excludePattern = Pattern.compile(excludeRegEx); } else { excludePattern = null; } final Iterator<MetaClass> filterIterator = result.iterator(); while (filterIterator.hasNext()) { _removeIfNotMatches(filterIterator, filterIterator.next(), packages, excludePattern); } } private static void filterResultsMethod(final Collection<MetaMethod> result, final Set<String> packages, final String excludeRegEx) { final Pattern excludePattern; if (excludeRegEx != null) { excludePattern = Pattern.compile(excludeRegEx); } else { excludePattern = null; } final Iterator<MetaMethod> filterIterator = result.iterator(); while (filterIterator.hasNext()) { _removeIfNotMatches(filterIterator, filterIterator.next().getDeclaringClass(), packages, excludePattern); } } private static void filterResultsField(final Collection<MetaField> result, final Set<String> packages, final String excludeRegEx) { final Pattern excludePattern; if (excludeRegEx != null) { excludePattern = Pattern.compile(excludeRegEx); } else { excludePattern = null; } final Iterator<MetaField> filterIterator = result.iterator(); while (filterIterator.hasNext()) { _removeIfNotMatches(filterIterator, filterIterator.next().getDeclaringClass(), packages, excludePattern); } } private static void filterResultsParameter(final Collection<MetaParameter> result, final Set<String> packages, final String excludeRegEx) { final Pattern excludePattern; if (excludeRegEx != null) { excludePattern = Pattern.compile(excludeRegEx); } else { excludePattern = null; } final Iterator<MetaParameter> filterIterator = result.iterator(); while (filterIterator.hasNext()) { _removeIfNotMatches(filterIterator, filterIterator.next().getDeclaringMember().getDeclaringClass(), packages, excludePattern); } } private static void _removeIfNotMatches(final Iterator<?> iterator, final MetaClass type, final Set<String> packages, final Pattern excludePattern) { if (packages == null || packages.contains(type.getPackageName())) { if (excludePattern != null && excludePattern.matcher(type.getFullyQualifiedName()).matches()) { iterator.remove(); } } else { iterator.remove(); } } public static void setReflectionsScanning(final boolean bool) { reflectionScanLocal.set(bool); } public static AtomicLong getTotalClassScanTime() { return totalClassScanTime; } }