/*************************************************************************************
* Copyright (c) 2015 Red Hat, Inc. and others.
* All rights reserved. This program and the accompanying materials
* are 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:
* JBoss by Red Hat - Initial implementation.
************************************************************************************/
package org.jboss.tools.batch.internal.core.scanner;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
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.batch.core.BatchArtifactType;
import org.jboss.tools.batch.core.BatchCorePlugin;
import org.jboss.tools.batch.core.IBatchProject;
import org.jboss.tools.batch.internal.core.impl.BatchBuilder;
import org.jboss.tools.batch.internal.core.impl.BatchProject;
import org.jboss.tools.batch.internal.core.impl.definition.TypeDefinition;
import org.jboss.tools.common.EclipseUtil;
import org.jboss.tools.common.core.jandex.JandexUtil;
import org.jboss.tools.common.util.FileUtil;
/**
*
* @author Viacheslav Kabanovich
*
*/
public class BatchArchiveDetector {
public static final int UNRESOLVED = -1;
public static final int NOT_ARCHIVE = 0;
public static final int ARCHIVE = 1;
static BatchArchiveDetector instance = new BatchArchiveDetector();
public static BatchArchiveDetector 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 BatchArchiveDetector() {}
/**
* 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 getBatchArchive(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 setBatchArchive(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) {
BatchCorePlugin.pluginLog().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) {
BatchCorePlugin.pluginLog().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() {
BatchCorePlugin plugin = BatchCorePlugin.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, IBatchProject project) throws JavaModelException {
File jarFile = new File(jar);
if(jarFile.isFile()) {
if(computeIsJarBatchArchive(jarFile)) {
setBatchArchive(jar, ARCHIVE);
} else {
setBatchArchive(jar, NOT_ARCHIVE);
}
} else if(jarFile.isDirectory()) {
IPackageFragmentRoot root = findPackageFragmentRoot(jar, project);
if (root != null && root.exists()) {
if(hasBatchArtifcts(root, project)) {
setBatchArchive(jar, ARCHIVE);
} else {
setBatchArchive(jar, NOT_ARCHIVE);
}
}
}
return getBatchArchive(jar);
}
boolean hasBatchArtifcts(IPackageFragmentRoot root, IBatchProject 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(isBatchArtifact(c.getType(), project)) {
return true;
}
}
}
}
//TODO check also jobs
return false;
}
public static boolean isBatchArtifact(IType type, IBatchProject project) throws JavaModelException {
TypeDefinition def = new TypeDefinition();
def.setType(type, ((BatchProject)project).getDefinitions(), TypeDefinition.FLAG_NO_ANNOTATIONS);
if(def.getArtifactType() != null) {
return true;
}
return false;
}
public static IPackageFragmentRoot findPackageFragmentRoot(String jar, IBatchProject project) {
IJavaProject jp = EclipseUtil.getJavaProject(project.getProject());
return (jp == null) ? null : findPackageFragmentRoot(jar, jp);
}
public static IPackageFragmentRoot findPackageFragmentRoot(String jar, IProject project) {
IJavaProject jp = EclipseUtil.getJavaProject(project);
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 = BatchBuilder.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;
}
static List<String> ARTIFACT_CLASSES = new ArrayList<String>();
static List<String> ARTIFACT_INTERFACES = new ArrayList<String>();
static {
for (BatchArtifactType t: BatchArtifactType.values()) {
String className = t.getClassName();
if(className != null) {
ARTIFACT_CLASSES.add(className);
}
className = t.getInterfaceName();
if(className != null) {
ARTIFACT_INTERFACES.add(className);
}
}
}
/**
* Use of Jandex in checking jar for subclasses of Batch artifact types
* is much faster than use of JDT.
*
* @param jarFile
* @return
*/
boolean computeIsJarBatchArchive(File jarFile) {
return JandexUtil.hasSubtypes(jarFile, ARTIFACT_CLASSES, ARTIFACT_INTERFACES);
}
}