/* * Copyright (c) 1998-2011 Caucho Technology -- all rights reserved * * This file is part of Resin(R) Open Source * * Each copy or derived work must preserve the copyright notice and this * notice unmodified. * * Resin Open Source is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Resin Open Source is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty * of NON-INFRINGEMENT. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License * along with Resin Open Source; if not, write to the * * Free Software Foundation, Inc. * 59 Temple Place, Suite 330 * Boston, MA 02111-1307 USA * * @author Scott Ferguson */ package com.caucho.config.inject; import java.lang.annotation.Annotation; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Level; import java.util.logging.Logger; import javax.enterprise.inject.spi.AnnotatedType; import com.caucho.config.program.ConfigProgram; import com.caucho.config.reflect.AnnotatedTypeImpl; import com.caucho.config.reflect.ReflectionAnnotatedFactory; import com.caucho.config.type.AnnotationType; import com.caucho.loader.DynamicClassLoader; import com.caucho.loader.EnvironmentClassLoader; import com.caucho.loader.enhancer.ScanClass; import com.caucho.loader.enhancer.ScanListener; import com.caucho.util.CharBuffer; import com.caucho.vfs.Path; /** * The web beans container for a given environment. */ class InjectScanManager implements ScanListener { private static final Logger log = Logger.getLogger(InjectScanManager.class.getName()); private InjectManager _injectManager; private final HashMap<Path, ScanRootContext> _scanRootMap = new HashMap<Path, ScanRootContext>(); private final ArrayList<ScanRootContext> _pendingScanRootList = new ArrayList<ScanRootContext>(); private final ConcurrentHashMap<String, InjectScanClass> _scanClassMap = new ConcurrentHashMap<String, InjectScanClass>(); private final ConcurrentHashMap<NameKey, AnnType> _annotationMap = new ConcurrentHashMap<NameKey, AnnType>(); private final ArrayList<InjectScanClass> _immediateResourceList = new ArrayList<InjectScanClass>(); private NameKey _nameKey = new NameKey(); private boolean _isCustomExtension; private ArrayList<InjectScanClass> _pendingScanClassList = new ArrayList<InjectScanClass>(); InjectScanManager(InjectManager injectManager) { _injectManager = injectManager; } /** * Returns the injection manager. */ public InjectManager getInjectManager() { return _injectManager; } /** * True if a custom extension exists. */ public void setIsCustomExtension(boolean isCustomExtension) { _isCustomExtension = isCustomExtension; if (isCustomExtension) { for (InjectScanClass scanClass : _scanClassMap.values()) { scanClass.register(); } } } public boolean isCustomExtension() { return _isCustomExtension; } public ArrayList<ScanRootContext> getPendingScanRootList() { ArrayList<ScanRootContext> contextList = new ArrayList<ScanRootContext>(_pendingScanRootList); _pendingScanRootList.clear(); return contextList; } public boolean isPending() { return _pendingScanClassList.size() > 0 || _pendingScanRootList.size() > 0; } public void addDiscoveredClass(InjectScanClass injectScanClass) { if (! _pendingScanClassList.contains(injectScanClass)) { _pendingScanClassList.add(injectScanClass); } } /** * discovers pending beans. */ public void discover() { ArrayList<InjectScanClass> pendingScanClassList = new ArrayList<InjectScanClass>(_pendingScanClassList); _pendingScanClassList.clear(); for (InjectScanClass scanClass : pendingScanClassList) { getInjectManager().discoverBean(scanClass); } } // // ScanListener /** * Since CDI doesn't enhance, it's priority 1 */ @Override public int getScanPriority() { return 1; } @Override public boolean isRootScannable(Path root, String packageRoot) { ScanRootContext context = _scanRootMap.get(root); List<Path> beansXmlOverride = _injectManager.getBeansXmlOverride(root); Path scanRoot = root; if (packageRoot != null) { scanRoot = scanRoot.lookup(packageRoot.replace('.', '/')); } if (beansXmlOverride == null) { // TODO Should resin-beans.xml be included in this check? if (packageRoot != null) { if (! scanRoot.lookup("beans.xml").canRead() && ! scanRoot.lookup("META-INF/beans.xml").canRead()) { return false; } } else if (! (root.lookup("META-INF/beans.xml").canRead() || (root.getFullPath().endsWith("WEB-INF/classes/") && root.lookup("../beans.xml").canRead()))) { return false; } } if (context == null) { context = new ScanRootContext(scanRoot, packageRoot); _scanRootMap.put(root, context); _pendingScanRootList.add(context); } if (context.isScanComplete()) return false; else { if (log.isLoggable(Level.FINER)) log.finer("CanDI scanning " + root.getURL()); context.setScanComplete(true); return true; } } /** * Checks if the class can be a simple class */ @Override public ScanClass scanClass(Path root, String packageRoot, String className, int modifiers) { // ioc/0j0k - package private allowed if (Modifier.isPrivate(modifiers)) return null; else { InjectScanClass scanClass = createScanClass(className); scanClass.setScanClass(); return scanClass; } } InjectScanClass getScanClass(String className) { return _scanClassMap.get(className); } InjectScanClass createScanClass(String className) { InjectScanClass scanClass = _scanClassMap.get(className); if (scanClass == null) { scanClass = new InjectScanClass(className, this); InjectScanClass oldScanClass; oldScanClass = _scanClassMap.putIfAbsent(className, scanClass); if (oldScanClass != null) scanClass = oldScanClass; } return scanClass; } /** * Loads an annotation for scanning. */ public AnnType loadAnnotation(char[] buffer, int offset, int length) throws ClassNotFoundException { NameKey key = _nameKey; key.init(buffer, offset, length); // new NameKey(buffer, offset, length); AnnType annType = _annotationMap.get(key); if (annType != null) return annType; ClassLoader loader = getInjectManager().getClassLoader(); String className = new String(buffer, offset, length); Class<?> cl = Class.forName(className, false, loader); annType = new AnnType(cl); _annotationMap.put(key.dup(), annType); return annType; } @Override public boolean isScanMatchAnnotation(CharBuffer string) { return false; } @Override public void classMatchEvent(EnvironmentClassLoader loader, Path root, String className) { } public void addImmediateResource(InjectScanClass injectScanClass) { ClassLoader classLoader = _injectManager.getClassLoader(); if (classLoader instanceof DynamicClassLoader) { DynamicClassLoader dynLoader = (DynamicClassLoader) classLoader; ClassLoader tmpLoader = dynLoader.getNewTempClassLoader(); Thread thread = Thread.currentThread(); ClassLoader oldLoader = thread.getContextClassLoader(); try { thread.setContextClassLoader(dynLoader); Class<?> cl = tmpLoader.loadClass(injectScanClass.getClassName()); AnnotatedType<?> annType = ReflectionAnnotatedFactory.introspectType(cl); for (Annotation ann : cl.getAnnotations()) { InjectionPointHandler handler = _injectManager.getInjectionPointHandler(ann.annotationType()); if (handler != null) { ConfigProgram program = handler.introspectType(annType); if (program != null) program.inject(null, null); } } } catch (Exception e) { log.log(Level.FINE, e.toString(), e); } finally { thread.setContextClassLoader(oldLoader); } } } @Override public String toString() { return getClass().getSimpleName() + "[" + _injectManager + "]"; } static class NameKey { private char[] _buffer; private int _offset; private int _length; NameKey(char[] buffer, int offset, int length) { init(buffer, offset, length); } NameKey() { } void init(char[] buffer, int offset, int length) { _buffer = buffer; _offset = offset; _length = length; } public NameKey dup() { char[] buffer = new char[_length]; System.arraycopy(_buffer, _offset, buffer, 0, _length); _buffer = buffer; _offset = 0; return this; } public int hashCode() { char[] buffer = _buffer; int offset = _offset; int length = _length; int hash = length; for (length--; length >= 0; length--) { char value = buffer[offset + length]; hash = 65521 * hash + value; } return hash; } public boolean equals(Object o) { if (!(o instanceof NameKey)) return false; NameKey key = (NameKey) o; if (_length != key._length) return false; char[] bufferA = _buffer; char[] bufferB = key._buffer; int offsetA = _offset; int offsetB = key._offset; for (int i = _length - 1; i >= 0; i--) { if (bufferA[offsetA + i] != bufferB[offsetB + i]) return false; } return true; } } static class AnnType { private Class<?> _type; private Annotation[] _annotations; AnnType(Class<?> type) { _type = type; } public Class<?> getType() { return _type; } public Annotation[] getAnnotations() { if (_annotations == null) _annotations = _type.getAnnotations(); return _annotations; } } }