package org.jkiss.dbeaver.registry.driver; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.jface.operation.IRunnableWithProgress; import org.jkiss.dbeaver.Log; import org.jkiss.dbeaver.model.connection.DBPDriver; import org.jkiss.dbeaver.model.connection.DBPDriverLibrary; import org.jkiss.utils.ArrayUtils; import org.jkiss.utils.CommonUtils; import org.objectweb.asm.ClassReader; import org.objectweb.asm.Opcodes; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; import java.util.Collection; import java.util.Enumeration; import java.util.List; import java.util.jar.JarEntry; import java.util.jar.JarFile; public class DriverClassFindJob implements IRunnableWithProgress { private static final Log log = Log.getLog(DriverClassFindJob.class); public static final String OBJECT_CLASS_NAME = "java/lang/Object"; public static final String CLASS_FILE_EXT = ".class"; private List<String> driverClassNames = new ArrayList<>(); private final DriverDescriptor driver; private final String interfaceName; private final boolean isInterface; public DriverClassFindJob(DBPDriver driver, String interfaceName, boolean isInterface) { this.driver = (DriverDescriptor) driver; this.interfaceName = interfaceName; this.isInterface = isInterface; } public List<String> getDriverClassNames() { return driverClassNames; } @Override public void run(IProgressMonitor monitor) { findDriverClasses(monitor); } private void findDriverClasses(IProgressMonitor monitor) { java.util.List<File> libFiles = new ArrayList<>(); java.util.List<URL> libURLs = new ArrayList<>(); for (DBPDriverLibrary lib : driver.getDriverLibraries()) { File libFile = lib.getLocalFile(); if (libFile != null && libFile.exists() && !libFile.isDirectory() && lib.getType() == DBPDriverLibrary.FileType.jar) { libFiles.add(libFile); try { libURLs.add(libFile.toURI().toURL()); } catch (MalformedURLException e) { log.debug(e); } } else { final Collection<DriverDescriptor.DriverFileInfo> files = driver.getLibraryFiles(lib); if (files != null) { for (DriverDescriptor.DriverFileInfo file : files) { if (file.getFile() != null && file.getFile().exists()) { libFiles.add(file.getFile()); } } } } } ClassLoader findCL = new URLClassLoader(libURLs.toArray(new URL[libURLs.size()])); for (File libFile : libFiles) { if (monitor.isCanceled()) { break; } findDriverClasses(monitor, findCL, libFile); } } private void findDriverClasses(IProgressMonitor monitor, ClassLoader findCL, File libFile) { try { JarFile currentFile = new JarFile(libFile, false); monitor.beginTask(libFile.getName(), currentFile.size()); for (Enumeration<?> e = currentFile.entries(); e.hasMoreElements(); ) { { if (monitor.isCanceled()) { break; } JarEntry current = (JarEntry) e.nextElement(); String fileName = current.getName(); if (fileName.endsWith(CLASS_FILE_EXT) && !fileName.contains("$")) { //$NON-NLS-1$ //$NON-NLS-2$ String className = fileName.replaceAll("/", ".").replace(CLASS_FILE_EXT, ""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ monitor.subTask(className); try { if (implementsInterface(currentFile, current, 0)) { driverClassNames.add(className); } } catch (Throwable e1) { // do nothing } monitor.worked(1); } } } monitor.done(); } catch (IOException e) { log.debug(e); } } private boolean implementsInterface(JarFile currentFile, JarEntry current, int depth) throws IOException { try (InputStream classStream = currentFile.getInputStream(current)) { ClassReader cr = new ClassReader(classStream); int access = cr.getAccess(); if (depth == 0 && ((access & Opcodes.ACC_PUBLIC) == 0 || (access & Opcodes.ACC_ABSTRACT) != 0)) { return false; } final String superName = cr.getSuperName(); if (isInterface) { String[] interfaces = cr.getInterfaces(); if (ArrayUtils.contains(interfaces, interfaceName)) { return true; } else if (!CommonUtils.isEmpty(superName) && !superName.equals(OBJECT_CLASS_NAME)) { // Check recursively JarEntry jarEntry = currentFile.getJarEntry(superName + CLASS_FILE_EXT); if (jarEntry != null) { return implementsInterface(currentFile, jarEntry, depth + 1); } } for (String intName : interfaces) { JarEntry jarEntry = currentFile.getJarEntry(intName + CLASS_FILE_EXT); if (jarEntry != null) { if (implementsInterface(currentFile, jarEntry, depth + 1)) { return true; } } } } else if (superName != null) { // Superclass if (interfaceName.equals(superName)) { return true; } JarEntry jarEntry = currentFile.getJarEntry(superName + CLASS_FILE_EXT); if (jarEntry != null) { if (implementsInterface(currentFile, jarEntry, depth + 1)) { return true; } } } } return false; } }