/******************************************************************************* * Copyright (c) 2011 Red Hat, Inc. * Distributed under license by Red Hat, Inc. All rights reserved. * This program is made available under the terms of the * Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Red Hat, Inc. - initial API and implementation ******************************************************************************/ package org.jboss.tools.cdi.internal.core.scanner.lib; import java.io.File; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.StringTokenizer; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; import org.eclipse.jdt.core.IAnnotation; import org.eclipse.jdt.core.IClassFile; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IPackageFragment; import org.eclipse.jdt.core.IPackageFragmentRoot; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.JavaModelException; import org.jboss.tools.cdi.core.CDIConstants; import org.jboss.tools.cdi.core.CDICoreNature; import org.jboss.tools.cdi.core.CDICorePlugin; import org.jboss.tools.cdi.core.CDIVersion; import org.jboss.tools.cdi.internal.core.impl.definition.AnnotationDefinition; import org.jboss.tools.common.core.jandex.JandexUtil; import org.jboss.tools.common.model.util.EclipseJavaUtil; import org.jboss.tools.common.model.util.EclipseResourceUtil; import org.jboss.tools.common.util.FileUtil; /** * Keeps list of all CDI 1.1 implicit bean archives that were checked if they have managed beans. * * @author Viacheslav Kabanovich * */ public class BeanArchiveDetector { public static final int UNRESOLVED = -1; public static final int NOT_ARCHIVE = 0; public static final int NONE = 1; public static final int ANNOTATED = 2; public static final int ALL = 3; public static BeanArchiveDetector instance = new BeanArchiveDetector(); public static BeanArchiveDetector getInstance() { return instance; } static class Result { int size; int archive = UNRESOLVED; Result(int size, int archive) { this.size = size; this.archive = archive; } } boolean isLoaded = false; boolean isDirty = false; Map<String, Result> paths = new HashMap<String, Result>(); private BeanArchiveDetector() { } /** * Returns NOT_ARCHIVE if path is tested not to be a bean archive, * UNRESOLVED if path is not tested yet, * NONE, ANNOTATED or ALL if path is tested to be a bean archive * @param path * @return */ public synchronized int getBeanArchive(String path) { load(); if(!paths.containsKey(path)) { return UNRESOLVED; } int size = getSize(path); if(size != paths.get(path).size) { paths.remove(path); isDirty = true; return UNRESOLVED; } return paths.get(path).archive; } public synchronized void setBeanArchive(String path, int archive) { load(); int size = getSize(path); if(size > 0) { paths.put(path, new Result(size, archive)); isDirty = true; } } private int getSize(String path) { return getSize(new File(path)); } private int getSize(File f) { if(f.isFile()) { return (int)f.length(); } else if(f.isDirectory()) { int result = 0; File[] fs = f.listFiles(); if(fs != null) { for (File c: fs) { result += getSize(c); } } return result; } return 0; } private synchronized void load() { if(isLoaded) return; try { File f = getStorageFile(); if(f.isFile()) { String content = FileUtil.readFile(f); StringTokenizer st = new StringTokenizer(content, "\n"); String path = null; int size = 0; int archive = UNRESOLVED; int c = 0; while(st.hasMoreTokens()) { String t = st.nextToken(); if(c == 0 && t.startsWith("path=")) { path = t.substring(5); c++; } else if(c == 1 && t.startsWith("size=")) { try { size = Integer.parseInt(t.substring(5)); c++; } catch (NumberFormatException e) { CDICorePlugin.getDefault().logError(e); } } else if(c == 2 && t.startsWith("archive=")) { try { archive = Integer.parseInt(t.substring(8)); if(getSize(path) == size) { paths.put(path, new Result(size, archive)); } c = 0; } catch (NumberFormatException e) { CDICorePlugin.getDefault().logError(e); } } } } } finally { isLoaded = true; } } public synchronized void save() { if(isLoaded && isDirty) { isDirty = false; File f = getStorageFile(); StringBuilder sb = new StringBuilder(); for (String path: paths.keySet()) { Result r = paths.get(path); sb.append("path=").append(path).append("\n") .append("size=").append(r.size).append("\n") .append("archive=").append(r.archive).append("\n"); } FileUtil.writeFile(f, sb.toString()); } } private File getStorageFile() { CDICorePlugin plugin = CDICorePlugin.getDefault(); if( plugin != null) { //The plug-in instance can be null at shutdown, when the plug-in is stopped. IPath path = plugin.getStateLocation(); File file = new File(path.toFile(), "bean-archives.txt"); //$NON-NLS-1$ return file; } else { return null; } } public int resolve(String jar, CDICoreNature project) throws JavaModelException { File jarFile = new File(jar); if (jarFile.isFile()) { if(hasAnnotatedBeans(jarFile, project)) { setBeanArchive(jar, ANNOTATED); } else { setBeanArchive(jar, NOT_ARCHIVE); } } else { IPackageFragmentRoot root = findPackageFragmentRoot(jar, project); if (root != null && root.exists()) { if(hasAnnotatedBeans(root, project)) { setBeanArchive(jar, ANNOTATED); } else { setBeanArchive(jar, NOT_ARCHIVE); } } } return getBeanArchive(jar); } /** * Returns true if at least one type in the archive is an annotated CDI 1.1 bean. * * @param root * @param project * @return * @throws JavaModelException */ public static boolean hasAnnotatedBeans(IPackageFragmentRoot root, CDICoreNature project) throws JavaModelException { IJavaElement[] es = root.getChildren(); for (IJavaElement e : es) { if (e instanceof IPackageFragment) { IPackageFragment pf = (IPackageFragment) e; IClassFile[] cs = pf.getClassFiles(); for (IClassFile c : cs) { if(isAnnotatedBean(c.getType(), project)) { return true; } } } } return false; } /** * Returns true if type has a scope annotation and therefore is a CDI 1.1 annotated bean. * * @param type * @param project * @return * @throws JavaModelException */ public static boolean isAnnotatedBean(IType type, CDICoreNature project) throws JavaModelException { for (IAnnotation a: type.getAnnotations()) { String typeName = EclipseJavaUtil.resolveType(type, a.getElementName()); if(isBeanAnnotation(typeName, project)) { return true; } } return false; } static boolean hasAnnotatedBeans(File jarFile, final CDICoreNature project) throws JavaModelException { JandexUtil.IAnnotationCheck check = new JandexUtil.IAnnotationCheck() { @Override public boolean isRelevant(String annotationType) { try { return isBeanAnnotation(annotationType, project); } catch (JavaModelException e) { CDICorePlugin.getDefault().logError(e); } return false; } }; return JandexUtil.hasAnnotation(jarFile, check); } static boolean isBeanAnnotation(String typeName, CDICoreNature project) throws JavaModelException { if(CDIConstants.STATELESS_ANNOTATION_TYPE_NAME.equals(typeName) || CDIConstants.SINGLETON_ANNOTATION_TYPE_NAME.equals(typeName)) { //session bean annotation return true; } if(CDIVersion.CDI_1_2.equals(project.getVersion())) { if(CDIConstants.DECORATOR_STEREOTYPE_TYPE_NAME.equals(typeName) || CDIConstants.INTERCEPTOR_ANNOTATION_TYPE_NAME.equals(typeName)) { return true; } } IType at = project.getType(typeName); if(at != null) { int k = project.getDefinitions().getAnnotationKind(at); if(k == AnnotationDefinition.SCOPE) { //scope annotation if(CDIConstants.DEPENDENT_ANNOTATION_TYPE_NAME.equals(typeName)) { return true; } AnnotationDefinition sa = project.getDefinitions().getAnnotation(at); return sa != null && sa.getAnnotation(CDIConstants.NORMAL_SCOPE_ANNOTATION_TYPE_NAME) != null; } if(CDIVersion.CDI_1_2.equals(project.getVersion()) && k == AnnotationDefinition.STEREOTYPE) { return true; } } return false; } public static boolean isQualifier(IType type) throws JavaModelException { if(!type.isAnnotation()) { return false; } IAnnotation[] as = type.getAnnotations(); for (IAnnotation a: as) { String typeName = EclipseJavaUtil.resolveType(type, a.getElementName()); if(CDIConstants.QUALIFIER_ANNOTATION_TYPE_NAME.equals(typeName)) { return true; } } return false; } /** * Annotations that define CDI annotation types: qualifiers, scopes, stereotypes, interceptor bindings. */ static Set<String> CDI_ANNOTATIONS = new HashSet<String>(); static { CDI_ANNOTATIONS.add(CDIConstants.QUALIFIER_ANNOTATION_TYPE_NAME); CDI_ANNOTATIONS.add(CDIConstants.SCOPE_ANNOTATION_TYPE_NAME); CDI_ANNOTATIONS.add(CDIConstants.NORMAL_SCOPE_ANNOTATION_TYPE_NAME); CDI_ANNOTATIONS.add(CDIConstants.STEREOTYPE_ANNOTATION_TYPE_NAME); CDI_ANNOTATIONS.add(CDIConstants.INTERCEPTOR_BINDING_ANNOTATION_TYPE_NAME); } /** * Scope or Qualifier or Stereotype * @param type * @return * @throws JavaModelException */ public static boolean isCDIAnnotation(IType type) throws JavaModelException { if(!type.isAnnotation()) { return false; } IAnnotation[] as = type.getAnnotations(); for (IAnnotation a: as) { String typeName = EclipseJavaUtil.resolveType(type, a.getElementName()); if(CDI_ANNOTATIONS.contains(typeName)) { return true; } } return false; } public static IType findPackageInfo(IClassFile[] cs) { for (IClassFile cf: cs) { IType c = cf.getType(); if("package-info".equals(c.getElementName())) { return c; } } return null; } public static boolean isVetoed(IType type) throws JavaModelException { IAnnotation[] as = type.getAnnotations(); for (IAnnotation a: as) { String typeName = EclipseJavaUtil.resolveType(type, a.getElementName()); if(CDIConstants.VETOED_ANNOTATION_TYPE_NAME.equals(typeName)) { return true; } } return false; } public static IType[] getAnnotatedTypes(IType[] ts,CDICoreNature project) throws CoreException { if(ts.length == 1 && (BeanArchiveDetector.isAnnotatedBean(ts[0], project) || BeanArchiveDetector.isCDIAnnotation(ts[0]))) { return ts; } List<IType> result = new ArrayList<IType>(); for (IType t: ts) { if(BeanArchiveDetector.isAnnotatedBean(t, project)) { result.add(t); } } return result.toArray(new IType[0]); } public static IPackageFragmentRoot findPackageFragmentRoot(String jar, CDICoreNature project) { IJavaProject jp = EclipseResourceUtil.getJavaProject(project.getProject()); return (jp == null) ? null : findPackageFragmentRoot(jar, jp); } public static IPackageFragmentRoot findPackageFragmentRoot(String jar, IJavaProject jp) { IPackageFragmentRoot root = jp.getPackageFragmentRoot(jar); if(root != null && !root.exists()) { IFile f = EclipseResourceUtil.getFile(jar); if(f != null && f.exists()) { root = jp.getPackageFragmentRoot(f); } else { IContainer c = ResourcesPlugin.getWorkspace().getRoot().getContainerForLocation(new Path(jar + "/META-INF").makeAbsolute()); if(c != null && c.exists()) { root = jp.getPackageFragmentRoot(c.getParent()); } } } return root; } }