/* * DBeaver - Universal Database Manager * Copyright (C) 2010-2017 Serge Rider (serge@jkiss.org) * * 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.jkiss.dbeaver.registry.driver; import org.eclipse.core.runtime.IConfigurationElement; import org.eclipse.core.runtime.Platform; import org.jkiss.code.NotNull; import org.jkiss.code.Nullable; import org.jkiss.dbeaver.DBException; import org.jkiss.dbeaver.DBeaverPreferences; import org.jkiss.dbeaver.Log; import org.jkiss.dbeaver.core.DBeaverCore; import org.jkiss.dbeaver.core.DBeaverUI; import org.jkiss.dbeaver.model.*; import org.jkiss.dbeaver.model.connection.*; import org.jkiss.dbeaver.model.impl.AbstractDescriptor; import org.jkiss.dbeaver.model.impl.PropertyDescriptor; import org.jkiss.dbeaver.model.meta.Property; import org.jkiss.dbeaver.model.navigator.meta.DBXTreeNode; import org.jkiss.dbeaver.model.preferences.DBPPreferenceStore; import org.jkiss.dbeaver.model.preferences.DBPPropertyDescriptor; import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor; import org.jkiss.dbeaver.model.runtime.DBRRunnableWithProgress; import org.jkiss.dbeaver.model.runtime.OSDescriptor; import org.jkiss.dbeaver.registry.*; import org.jkiss.dbeaver.registry.maven.MavenArtifactReference; import org.jkiss.dbeaver.ui.UITask; import org.jkiss.dbeaver.ui.dialogs.AcceptLicenseDialog; import org.jkiss.dbeaver.ui.dialogs.driver.DriverDownloadDialog; import org.jkiss.dbeaver.utils.ContentUtils; import org.jkiss.dbeaver.utils.GeneralUtils; import org.jkiss.dbeaver.utils.SystemVariablesResolver; import org.jkiss.utils.CommonUtils; import org.jkiss.utils.StandardConstants; import org.jkiss.utils.xml.SAXListener; import org.jkiss.utils.xml.SAXReader; import org.jkiss.utils.xml.XMLBuilder; import org.jkiss.utils.xml.XMLException; import org.xml.sax.Attributes; import java.io.File; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.net.MalformedURLException; import java.net.URL; import java.util.*; /** * DriverDescriptor */ public class DriverDescriptor extends AbstractDescriptor implements DBPDriver { private static final Log log = Log.getLog(DriverDescriptor.class); private static final String DRIVERS_FOLDER = "drivers"; //$NON-NLS-1$ private static final String PROP_DRIVERS_LOCATION = "DRIVERS_LOCATION"; private static final char URL_GROUP_START = '{'; //$NON-NLS-1$ private static final char URL_GROUP_END = '}'; //$NON-NLS-1$ private static final char URL_OPTIONAL_START = '['; //$NON-NLS-1$ private static final char URL_OPTIONAL_END = ']'; //$NON-NLS-1$ public static final String PROP_HOST = "host"; //$NON-NLS-1$ public static final String PROP_PORT = "port"; //$NON-NLS-1$ public static final String PROP_DATABASE = "database"; //$NON-NLS-1$ public static final String PROP_SERVER = "server"; //$NON-NLS-1$ public static final String PROP_FOLDER = "folder"; //$NON-NLS-1$ public static final String PROP_FILE = "file"; //$NON-NLS-1$ public static final String PROP_USER = "user"; //$NON-NLS-1$ public static final String PROP_PASSWORD = "password"; //$NON-NLS-1$ private static final String LICENSE_ACCEPT_KEY = "driver.license.accept."; public static class DriverFileInfo { private final String id; private final String version; private final File file; DriverFileInfo(String id, String version, File file) { this.id = id; this.version = version; this.file = file; } DriverFileInfo(DBPDriverLibrary library) { this.id = library.getId(); this.version = library.getVersion(); this.file = library.getLocalFile(); } public File getFile() { return file; } @Override public String toString() { return file.getName(); } } private final DataSourceProviderDescriptor providerDescriptor; private final String id; private String category; private final String origName; private final String origDescription; private final String origClassName; private final String origDefaultPort; private final String origSampleURL; private String name; private String description; private String driverClassName; private String driverDefaultPort; private String sampleURL; private String webURL; private DBPImage iconPlain; private DBPImage iconNormal; private DBPImage iconError; private boolean embedded; private boolean clientRequired; private boolean supportsDriverProperties; private boolean anonymousAccess; private boolean customDriverLoader; private boolean custom; private boolean modified; private boolean disabled; private final List<String> clientHomeIds = new ArrayList<>(); private final List<DriverFileSource> fileSources = new ArrayList<>(); private final List<DBPDriverLibrary> libraries = new ArrayList<>(); private final List<DBPDriverLibrary> origFiles = new ArrayList<>(); private final List<DBPPropertyDescriptor> connectionPropertyDescriptors = new ArrayList<>(); private final List<OSDescriptor> supportedSystems = new ArrayList<>(); private final List<ReplaceInfo> driverReplacements = new ArrayList<>(); private DriverDescriptor replacedBy; private final Map<Object, Object> defaultParameters = new HashMap<>(); private final Map<Object, Object> customParameters = new HashMap<>(); private final Map<Object, Object> defaultConnectionProperties = new HashMap<>(); private final Map<Object, Object> customConnectionProperties = new HashMap<>(); private Map<DBPDriverLibrary, List<DriverFileInfo>> resolvedFiles = new HashMap<>(); private Class driverClass; private boolean isLoaded; private Object driverInstance; private DriverClassLoader classLoader; private transient boolean isFailed = false; static { File driversHome = DriverDescriptor.getCustomDriversHome(); System.setProperty(PROP_DRIVERS_LOCATION, driversHome.getAbsolutePath()); } // New driver constructor public DriverDescriptor(DataSourceProviderDescriptor providerDescriptor, String id) { super(providerDescriptor.getPluginId()); this.providerDescriptor = providerDescriptor; this.id = id; this.custom = true; this.iconPlain = providerDescriptor.getIcon(); if (this.iconPlain == null) { this.iconPlain = DBIcon.TREE_DATABASE; } makeIconExtensions(); this.origName = null; this.origDescription = null; this.origClassName = null; this.origDefaultPort = null; this.origSampleURL = null; } // Predefined driver constructor public DriverDescriptor(DataSourceProviderDescriptor providerDescriptor, IConfigurationElement config) { super(providerDescriptor.getPluginId()); this.providerDescriptor = providerDescriptor; this.id = CommonUtils.notEmpty(config.getAttribute(RegistryConstants.ATTR_ID)); this.category = CommonUtils.notEmpty(config.getAttribute(RegistryConstants.ATTR_CATEGORY)); this.origName = this.name = CommonUtils.notEmpty(config.getAttribute(RegistryConstants.ATTR_LABEL)); this.origDescription = this.description = config.getAttribute(RegistryConstants.ATTR_DESCRIPTION); this.origClassName = this.driverClassName = config.getAttribute(RegistryConstants.ATTR_CLASS); if (!CommonUtils.isEmpty(config.getAttribute(RegistryConstants.ATTR_DEFAULT_PORT))) { this.origDefaultPort = this.driverDefaultPort = config.getAttribute(RegistryConstants.ATTR_DEFAULT_PORT); } else { this.origDefaultPort = this.driverDefaultPort = null; } this.origSampleURL = this.sampleURL = config.getAttribute(RegistryConstants.ATTR_SAMPLE_URL); this.webURL = config.getAttribute(RegistryConstants.ATTR_WEB_URL); this.clientRequired = CommonUtils.getBoolean(config.getAttribute(RegistryConstants.ATTR_CLIENT_REQUIRED), false); this.customDriverLoader = CommonUtils.getBoolean(config.getAttribute(RegistryConstants.ATTR_CUSTOM_DRIVER_LOADER), false); this.supportsDriverProperties = CommonUtils.getBoolean(config.getAttribute(RegistryConstants.ATTR_SUPPORTS_DRIVER_PROPERTIES), true); this.embedded = CommonUtils.getBoolean(config.getAttribute(RegistryConstants.ATTR_EMBEDDED)); this.anonymousAccess = CommonUtils.getBoolean(config.getAttribute(RegistryConstants.ATTR_ANONYMOUS)); this.custom = false; this.isLoaded = false; for (IConfigurationElement lib : config.getChildren(RegistryConstants.TAG_FILE)) { DriverLibraryAbstract library = DriverLibraryAbstract.createFromConfig(this, lib); if (library != null) { this.libraries.add(library); } } this.origFiles.addAll(this.libraries); for (IConfigurationElement lib : config.getChildren(RegistryConstants.TAG_FILE_SOURCE)) { this.fileSources.add(new DriverFileSource(lib)); } this.iconPlain = iconToImage(config.getAttribute(RegistryConstants.ATTR_ICON)); if (this.iconPlain == null) { this.iconPlain = providerDescriptor.getIcon(); } makeIconExtensions(); { // OSes IConfigurationElement[] osElements = config.getChildren(RegistryConstants.TAG_OS); for (IConfigurationElement os : osElements) { supportedSystems.add(new OSDescriptor( os.getAttribute(RegistryConstants.ATTR_NAME), os.getAttribute(RegistryConstants.ATTR_ARCH) )); } } { // Connection property groups IConfigurationElement[] propElements = config.getChildren(PropertyDescriptor.TAG_PROPERTY_GROUP); for (IConfigurationElement prop : propElements) { connectionPropertyDescriptors.addAll(PropertyDescriptor.extractProperties(prop)); } } { // Driver parameters IConfigurationElement[] paramElements = config.getChildren(RegistryConstants.TAG_PARAMETER); for (IConfigurationElement param : paramElements) { String paramName = param.getAttribute(RegistryConstants.ATTR_NAME); String paramValue = param.getAttribute(RegistryConstants.ATTR_VALUE); if (CommonUtils.isEmpty(paramValue)) { paramValue = param.getValue(); } if (!CommonUtils.isEmpty(paramName) && !CommonUtils.isEmpty(paramValue)) { setDriverParameter(paramName, paramValue, true); } } } { // Connection properties IConfigurationElement[] propElements = config.getChildren(RegistryConstants.TAG_PROPERTY); for (IConfigurationElement param : propElements) { String paramName = param.getAttribute(RegistryConstants.ATTR_NAME); String paramValue = param.getAttribute(RegistryConstants.ATTR_VALUE); if (CommonUtils.isEmpty(paramValue)) { paramValue = param.getValue(); } if (!CommonUtils.isEmpty(paramName) && !CommonUtils.isEmpty(paramValue)) { defaultConnectionProperties.put(paramName, paramValue); if (!paramName.startsWith(DBConstants.INTERNAL_PROP_PREFIX)) { customConnectionProperties.put(paramName, paramValue); } } } } { // Driver replacements IConfigurationElement[] replaceElements = config.getChildren(RegistryConstants.TAG_REPLACE); for (IConfigurationElement replace : replaceElements) { String providerId = replace.getAttribute(RegistryConstants.ATTR_PROVIDER); String driverId = replace.getAttribute(RegistryConstants.ATTR_DRIVER); if (!CommonUtils.isEmpty(providerId) && !CommonUtils.isEmpty(driverId)) { driverReplacements.add(new ReplaceInfo(providerId, driverId)); } } } } public DriverDescriptor getReplacedBy() { return replacedBy; } public void setReplacedBy(DriverDescriptor replaceBy) { this.replacedBy = replaceBy; } public boolean replaces(DriverDescriptor driver) { for (ReplaceInfo replaceInfo : driverReplacements) { if (driver.getProviderDescriptor().getId().equals(replaceInfo.providerId) && driver.getId().equals(replaceInfo.driverId)) { return true; } } return false; } private void makeIconExtensions() { if (isCustom()) { this.iconNormal = new DBIconComposite(this.iconPlain, false, null, null, DBIcon.OVER_LAMP, null); } else { this.iconNormal = this.iconPlain; } this.iconError = new DBIconComposite(this.iconPlain, false, null, null, isCustom() ? DBIcon.OVER_LAMP : null, DBIcon.OVER_ERROR); } @Nullable @Override public DriverClassLoader getClassLoader() { return classLoader; } public List<DataSourceDescriptor> getUsedBy() { List<DataSourceDescriptor> usedBy = new ArrayList<>(); for (DataSourceDescriptor ds : DataSourceRegistry.getAllDataSources()) { if (ds.getDriver() == this) { usedBy.add(ds); } } return usedBy; } public DataSourceProviderDescriptor getProviderDescriptor() { return providerDescriptor; } @NotNull @Override public DBPDataSourceProvider getDataSourceProvider() { return providerDescriptor.getInstance(this); } @Nullable @Override public DBPClientManager getClientManager() { DBPDataSourceProvider provider = getDataSourceProvider(); if (provider instanceof DBPClientManager) { return (DBPClientManager) provider; } else { return null; } } @NotNull @Override public String getId() { return id; } @Property(viewable = true, order = 2) public String getCategory() { return category; } public void setCategory(String category) { this.category = category; } @NotNull @Override @Property(viewable = true, order = 1) public String getName() { return name; } public void setName(String name) { this.name = name; } @Override @Property(viewable = true, order = 100) public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } @NotNull public String getFullName() { if (CommonUtils.isEmpty(category)) { return name; } else { return category + " / " + name; } } /** * Plain icon (without any overlays). * @return plain icon */ @NotNull public DBPImage getPlainIcon() { return iconPlain; } /** * Driver icon, includes overlays for driver conditions (custom, invalid, etc).. * @return icon */ @NotNull @Override public DBPImage getIcon() { if (!isLoaded && isFailed) { return iconError; } else { return iconNormal; } } public boolean isCustom() { return custom; } public boolean isModified() { return modified; } public void setModified(boolean modified) { this.modified = modified; } public boolean isDisabled() { return disabled; } public void setDisabled(boolean disabled) { this.disabled = disabled; } @Nullable @Override @Property(viewable = true, order = 2) public String getDriverClassName() { return driverClassName; } public void setDriverClassName(String driverClassName) { if (this.driverClassName == null || !this.driverClassName.equals(driverClassName)) { this.driverClassName = driverClassName; resetDriverInstance(); } } @NotNull @Override public Object getDriverInstance(@NotNull DBRProgressMonitor monitor) throws DBException { if (driverInstance == null) { loadDriver(monitor); } if (isInternalDriver() && driverInstance == null) { return createDriverInstance(); } return driverInstance; } private void resetDriverInstance() { this.driverInstance = null; this.driverClass = null; this.isLoaded = false; this.resolvedFiles.clear(); } private Object createDriverInstance() throws DBException { try { return driverClass.newInstance(); } catch (InstantiationException ex) { throw new DBException("Can't instantiate driver class", ex); } catch (IllegalAccessException ex) { throw new DBException("Illegal access", ex); } catch (ClassCastException ex) { throw new DBException("Bad driver class name specified", ex); } catch (Throwable ex) { throw new DBException("Error during driver instantiation", ex); } } @Nullable @Override public String getDefaultPort() { return driverDefaultPort; } public void setDriverDefaultPort(String driverDefaultPort) { this.driverDefaultPort = driverDefaultPort; } @Nullable @Override @Property(viewable = true, order = 3) public String getSampleURL() { return sampleURL; } public void setSampleURL(String sampleURL) { this.sampleURL = sampleURL; } @Nullable @Override public String getWebURL() { return webURL; } @Override public boolean isClientRequired() { return clientRequired; } @Override public boolean supportsDriverProperties() { return this.supportsDriverProperties; } @Override public boolean isEmbedded() { return embedded; } public void setEmbedded(boolean embedded) { this.embedded = embedded; } @Override public boolean isAnonymousAccess() { return anonymousAccess; } @Override public boolean isCustomDriverLoader() { return customDriverLoader; } @Nullable @Override public DBXTreeNode getNavigatorRoot() { return providerDescriptor.getTreeDescriptor(); } void setCustomDriverLoader(boolean customDriverLoader) { this.customDriverLoader = customDriverLoader; } public boolean isManagable() { return getProviderDescriptor().isDriversManagable(); } @Override public boolean isInternalDriver() { return driverClassName != null && driverClassName.contains("sun.jdbc"); //$NON-NLS-1$ } @NotNull @Override public Collection<String> getClientHomeIds() { return clientHomeIds; } public void setClientHomeIds(Collection<String> homeIds) { clientHomeIds.clear(); clientHomeIds.addAll(homeIds); } void addClientHomeId(String homeId) { clientHomeIds.add(homeId); } @NotNull @Override public Collection<? extends DBPDriverLibrary> getDriverLibraries() { return libraries; } public List<DBPDriverLibrary> getEnabledDriverLibraries() { List<DBPDriverLibrary> filtered = new ArrayList<>(); for (DBPDriverLibrary lib : libraries) { if (!lib.isDisabled()) { filtered.add(lib); } } return filtered; } DBPDriverLibrary getDriverLibrary(String path) { for (DBPDriverLibrary lib : libraries) { if (lib.getPath().equals(path)) { return lib; } } return null; } private void addLibraryFile(DBPDriverLibrary library, DriverFileInfo fileInfo) { List<DriverFileInfo> files = resolvedFiles.get(library); if (files == null) { files = new ArrayList<>(); resolvedFiles.put(library, files); } files.add(fileInfo); } public DBPDriverLibrary addDriverLibrary(String path, DBPDriverLibrary.FileType fileType) { for (DBPDriverLibrary lib : libraries) { if (lib.getPath().equals(path)) { return lib; } } DriverLibraryAbstract lib = DriverLibraryAbstract.createFromPath(this, fileType, path, null); addDriverLibrary(lib); return lib; } public boolean addDriverLibrary(DBPDriverLibrary descriptor) { if (!libraries.contains(descriptor)) { resetDriverInstance(); this.libraries.add(descriptor); return true; } return false; } public boolean removeDriverLibrary(DBPDriverLibrary lib) { resetDriverInstance(); if (!lib.isCustom()) { lib.setDisabled(true); return true; } else { return this.libraries.remove(lib); } } @NotNull public List<DriverFileSource> getDriverFileSources() { return fileSources; } @NotNull @Override public List<DBPPropertyDescriptor> getConnectionPropertyDescriptors() { return connectionPropertyDescriptors; } @NotNull @Override public Map<Object, Object> getDefaultConnectionProperties() { return defaultConnectionProperties; } @NotNull @Override public Map<Object, Object> getConnectionProperties() { return customConnectionProperties; } public void setConnectionProperty(String name, String value) { customConnectionProperties.put(name, value); } public void setConnectionProperties(Map<Object, Object> parameters) { customConnectionProperties.clear(); customConnectionProperties.putAll(parameters); } public Map<Object, Object> getDefaultDriverParameters() { return defaultParameters; } @NotNull @Override public Map<Object, Object> getDriverParameters() { return customParameters; } @Nullable @Override public Object getDriverParameter(String name) { return customParameters.get(name); } public void setDriverParameter(String name, String value, boolean setDefault) { DBPPropertyDescriptor prop = getProviderDescriptor().getDriverProperty(name); Object valueObject = prop == null ? value : GeneralUtils.convertString(value, prop.getDataType()); customParameters.put(name, valueObject); if (setDefault) { defaultParameters.put(name, valueObject); } } public void setDriverParameters(Map<Object, Object> parameters) { customParameters.clear(); customParameters.putAll(parameters); } @Override public boolean isSupportedByLocalSystem() { if (supportedSystems.isEmpty()) { // Multi-platform return true; } OSDescriptor localSystem = DBeaverCore.getInstance().getLocalSystem(); for (OSDescriptor system : supportedSystems) { if (system.matches(localSystem)) { return true; } } return false; } public String getLicense() { for (DBPDriverLibrary file : libraries) { if (file.getType() == DBPDriverLibrary.FileType.license) { final File licenseFile = file.getLocalFile(); if (licenseFile != null && licenseFile.exists()) { try { return ContentUtils.readFileToString(licenseFile); } catch (IOException e) { log.warn(e); } } } } return null; } @Override public void loadDriver(DBRProgressMonitor monitor) throws DBException { this.loadDriver(monitor, false); } private void loadDriver(DBRProgressMonitor monitor, boolean forceReload) throws DBException { if (isLoaded && !forceReload) { return; } isLoaded = false; loadLibraries(); if (!acceptDriverLicenses()) { throw new DBException("You have to accept driver '" + getFullName() + "' license to be able to connect"); } try { if (!isCustomDriverLoader()) { try { // Load driver classes into core module using plugin class loader driverClass = Class.forName(driverClassName, true, classLoader); } catch (Throwable ex) { throw new DBException("Can't load driver class '" + driverClassName + "'", ex); } // Create driver instance /*if (!this.isInternalDriver())*/ { driverInstance = createDriverInstance(); } isLoaded = true; isFailed = false; } } catch (DBException e) { isFailed = true; throw e; } } private void loadLibraries() throws DBException { this.classLoader = null; List<File> allLibraryFiles = validateFilesPresence(false); List<URL> libraryURLs = new ArrayList<>(); // Load libraries for (File file : allLibraryFiles) { URL url; try { url = file.toURI().toURL(); } catch (MalformedURLException e) { log.error(e); continue; } libraryURLs.add(url); } // Make class loader this.classLoader = new DriverClassLoader( this, libraryURLs.toArray(new URL[libraryURLs.size()]), getDataSourceProvider().getClass().getClassLoader()); } public void updateFiles() { validateFilesPresence(true); } @NotNull private List<File> validateFilesPresence(boolean resetVersions) { boolean localLibsExists = false; final List<DBPDriverLibrary> downloadCandidates = new ArrayList<>(); for (DBPDriverLibrary library : libraries) { if (library.isDisabled()) { // Nothing we can do about it continue; } if (!library.matchesCurrentPlatform()) { // Wrong OS or architecture continue; } if (library.isDownloadable()) { boolean allExists = true; if (resetVersions) { allExists = false; } else { List<DriverFileInfo> files = resolvedFiles.get(library); if (files == null) { allExists = false; } else { for (DriverFileInfo file : files) { if (file.file == null || !file.file.exists()) { allExists = false; break; } } } } if (!allExists) { downloadCandidates.add(library); } } else { localLibsExists = true; } } // if (!CommonUtils.isEmpty(fileSources)) { // for (DriverFileSource source : fileSources) { // for (DriverFileSource.FileInfo fileInfo : source.getFiles()) { // DriverLibraryLocal libraryLocal = new DriverLibraryLocal(this, DBPDriverLibrary.FileType.jar, fileInfo.getName()); // final File localFile = libraryLocal.getLocalFile(); // } // } // } boolean downloaded = false; if (!downloadCandidates.isEmpty() || (!localLibsExists && !fileSources.isEmpty())) { final DriverDependencies dependencies = new DriverDependencies(downloadCandidates); boolean downloadOk = new UITask<Boolean>() { @Override protected Boolean runTask() { return DriverDownloadDialog.downloadDriverFiles(null, DriverDescriptor.this, dependencies); } }.execute(); if (!downloadOk) { return Collections.emptyList(); } if (resetVersions) { resetDriverInstance(); /* for (DBPDriverLibrary library : libraries) { if (!library.isDisabled()) { library.resetVersion(); } } */ } downloaded = true; for (DBPDriverDependencies.DependencyNode node : dependencies.getLibraryMap()) { List<DriverFileInfo> info = new ArrayList<>(); resolvedFiles.put(node.library, info); collectLibraryFiles(node, info); } providerDescriptor.getRegistry().saveDrivers(); } List<File> result = new ArrayList<>(); for (DBPDriverLibrary library : libraries) { if (library.isDisabled() || !library.matchesCurrentPlatform()) { // Wrong OS or architecture continue; } if (library.isDownloadable()) { List<DriverFileInfo> files = resolvedFiles.get(library); if (files != null) { for (DriverFileInfo file : files) { result.add(file.file); } } } else { result.add(library.getLocalFile()); } } // Now check driver version if (DBeaverCore.getGlobalPreferenceStore().getBoolean(DBeaverPreferences.UI_DRIVERS_VERSION_UPDATE) && !downloaded) { // TODO: implement new version check if (false) { try { DBeaverUI.runInProgressService(new DBRRunnableWithProgress() { @Override public void run(DBRProgressMonitor monitor) throws InvocationTargetException, InterruptedException { try { checkDriverVersion(monitor); } catch (IOException e) { throw new InvocationTargetException(e); } } }); } catch (InvocationTargetException e) { log.error(e.getTargetException()); } catch (InterruptedException e) { // ignore } } } return result; } private void checkDriverVersion(DBRProgressMonitor monitor) throws IOException { for (DBPDriverLibrary library : libraries) { final Collection<String> availableVersions = library.getAvailableVersions(monitor); if (!CommonUtils.isEmpty(availableVersions)) { final String curVersion = library.getVersion(); String latestVersion = DriverUtils.findLatestVersion(availableVersions); if (latestVersion != null && !latestVersion.equals(curVersion)) { log.debug("Update driver " + getName() + " " + curVersion + "->" + latestVersion); } } } } public boolean isLibraryResolved(DBPDriverLibrary library) { return !library.isDownloadable() || !CommonUtils.isEmpty(resolvedFiles.get(library)); } public Collection<DriverFileInfo> getLibraryFiles(DBPDriverLibrary library) { return resolvedFiles.get(library); } private void collectLibraryFiles(DBPDriverDependencies.DependencyNode node, List<DriverFileInfo> files) { if (node.duplicate) { return; } files.add(new DriverFileInfo(node.library)); for (DBPDriverDependencies.DependencyNode sub : node.dependencies) { collectLibraryFiles(sub, files); } } public boolean acceptDriverLicenses() { /* // User must accept all licenses before actual drivers download for (final DBPDriverLibrary file : libraries) { if (file.getType() == DBPDriverLibrary.FileType.license) { final File libraryFile = file.getLocalFile(); if (libraryFile == null || !libraryFile.exists()) { try { runnableContext.run(true, true, new DBRRunnableWithProgress() { @Override public void run(DBRProgressMonitor monitor) throws InvocationTargetException, InterruptedException { try { file.downloadLibraryFile(monitor, false); } catch (final Exception e) { log.warn("Can't obtain driver license", e); } } }); } catch (Exception e) { log.warn(e); } } } } String licenseText = getLicense(); if (!CommonUtils.isEmpty(licenseText)) { return acceptLicense(licenseText); } */ // No license return true; } private boolean acceptLicense(String licenseText) { // Check registry DBPPreferenceStore prefs = DBeaverCore.getGlobalPreferenceStore(); String acceptedStr = prefs.getString(LICENSE_ACCEPT_KEY + getId()); if (!CommonUtils.isEmpty(acceptedStr)) { return true; } LicenceAcceptor licenceAcceptor = new LicenceAcceptor(licenseText); DBeaverUI.syncExec(licenceAcceptor); if (licenceAcceptor.result) { // Save in registry prefs.setValue(LICENSE_ACCEPT_KEY + getId(), true + ":" + System.currentTimeMillis() + ":" + System.getProperty(StandardConstants.ENV_USER_NAME)); return true; } return false; } public String getOrigName() { return origName; } public String getOrigDescription() { return origDescription; } public String getOrigClassName() { return origClassName; } public String getOrigDefaultPort() { return origDefaultPort; } public String getOrigSampleURL() { return origSampleURL; } public List<DBPDriverLibrary> getOrigFiles() { return origFiles; } public static File getDriversContribFolder() throws IOException { return new File(Platform.getInstallLocation().getDataArea(DRIVERS_FOLDER).toExternalForm()); } public void serialize(XMLBuilder xml, boolean export) throws IOException { Map<String, String> pathSubstitutions = new HashMap<>(); { DriverVariablesResolver varResolver = new DriverVariablesResolver(); String[] variables = new String[]{ DriverVariablesResolver.VAR_DRIVERS_HOME, SystemVariablesResolver.VAR_WORKSPACE, SystemVariablesResolver.VAR_HOME, SystemVariablesResolver.VAR_DBEAVER_HOME}; for (String varName : variables) { String varValue = varResolver.get(varName); if (!CommonUtils.isEmpty(varValue)) { pathSubstitutions.put(varValue, varName); } } } try (XMLBuilder.Element e0 = xml.startElement(RegistryConstants.TAG_DRIVER)) { if (export) { xml.addAttribute(RegistryConstants.ATTR_PROVIDER, providerDescriptor.getId()); } xml.addAttribute(RegistryConstants.ATTR_ID, this.getId()); if (this.isDisabled()) { xml.addAttribute(RegistryConstants.ATTR_DISABLED, true); } if (!CommonUtils.isEmpty(this.getCategory())) { xml.addAttribute(RegistryConstants.ATTR_CATEGORY, this.getCategory()); } xml.addAttribute(RegistryConstants.ATTR_CUSTOM, this.isCustom()); xml.addAttribute(RegistryConstants.ATTR_EMBEDDED, this.isEmbedded()); xml.addAttribute(RegistryConstants.ATTR_NAME, this.getName()); xml.addAttribute(RegistryConstants.ATTR_CLASS, this.getDriverClassName()); if (!CommonUtils.isEmpty(this.getSampleURL())) { xml.addAttribute(RegistryConstants.ATTR_URL, this.getSampleURL()); } if (this.getDefaultPort() != null) { xml.addAttribute(RegistryConstants.ATTR_PORT, this.getDefaultPort()); } xml.addAttribute(RegistryConstants.ATTR_DESCRIPTION, CommonUtils.notEmpty(this.getDescription())); if (this.isCustomDriverLoader()) { xml.addAttribute(RegistryConstants.ATTR_CUSTOM_DRIVER_LOADER, this.isCustomDriverLoader()); } // Libraries for (DBPDriverLibrary lib : libraries) { if (export && !lib.isDisabled()) { continue; } try (XMLBuilder.Element e1 = xml.startElement(RegistryConstants.TAG_LIBRARY)) { xml.addAttribute(RegistryConstants.ATTR_TYPE, lib.getType().name()); xml.addAttribute(RegistryConstants.ATTR_PATH, substitutePathVariables(pathSubstitutions, lib.getPath())); xml.addAttribute(RegistryConstants.ATTR_CUSTOM, lib.isCustom()); if (lib.isDisabled()) { xml.addAttribute(RegistryConstants.ATTR_DISABLED, true); } if (!CommonUtils.isEmpty(lib.getPreferredVersion())) { xml.addAttribute(RegistryConstants.ATTR_VERSION, lib.getPreferredVersion()); } //xml.addAttribute(RegistryConstants.ATTR_CUSTOM, lib.isCustom()); List<DriverFileInfo> files = resolvedFiles.get(lib); if (files != null) { for (DriverFileInfo file : files) { try (XMLBuilder.Element e2 = xml.startElement(RegistryConstants.TAG_FILE)) { if (file.file == null) { log.warn("File missing in " + file.id); continue; } xml.addAttribute(RegistryConstants.ATTR_ID, file.id); if (!CommonUtils.isEmpty(file.version)) { xml.addAttribute(RegistryConstants.ATTR_VERSION, file.version); } xml.addAttribute(RegistryConstants.ATTR_PATH, substitutePathVariables(pathSubstitutions, file.file.getAbsolutePath())); } } } } } // Client homes for (String homeId : clientHomeIds) { try (XMLBuilder.Element e1 = xml.startElement(RegistryConstants.TAG_CLIENT_HOME)) { xml.addAttribute(RegistryConstants.ATTR_ID, homeId); } } // Parameters for (Map.Entry<Object, Object> paramEntry : customParameters.entrySet()) { if (!CommonUtils.equalObjects(paramEntry.getValue(), defaultParameters.get(paramEntry.getKey()))) { try (XMLBuilder.Element e1 = xml.startElement(RegistryConstants.TAG_PARAMETER)) { xml.addAttribute(RegistryConstants.ATTR_NAME, CommonUtils.toString(paramEntry.getKey())); xml.addAttribute(RegistryConstants.ATTR_VALUE, CommonUtils.toString(paramEntry.getValue())); } } } // Properties for (Map.Entry<Object, Object> propEntry : customConnectionProperties.entrySet()) { if (!CommonUtils.equalObjects(propEntry.getValue(), defaultConnectionProperties.get(propEntry.getKey()))) { try (XMLBuilder.Element e1 = xml.startElement(RegistryConstants.TAG_PROPERTY)) { xml.addAttribute(RegistryConstants.ATTR_NAME, CommonUtils.toString(propEntry.getKey())); xml.addAttribute(RegistryConstants.ATTR_VALUE, CommonUtils.toString(propEntry.getValue())); } } } } } @Nullable @Override public DBPClientHome getClientHome(String homeId) { DBPClientManager clientManager = getClientManager(); if (clientManager != null) { return clientManager.getClientHome(homeId); } return null; } public String getDefaultClientHomeId() { DBPClientManager clientManager = getClientManager(); if (clientManager != null) { return clientManager.getDefaultClientHomeId(); } return null; } public static File getCustomDriversHome() { File homeFolder; // Try to use custom drivers path from preferences String driversHome = DBeaverCore.getGlobalPreferenceStore().getString(DBeaverPreferences.UI_DRIVERS_HOME); if (!CommonUtils.isEmpty(driversHome)) { homeFolder = new File(driversHome); } else { homeFolder = new File( System.getProperty(StandardConstants.ENV_USER_HOME), DBConstants.DEFAULT_DRIVERS_FOLDER); } if (!homeFolder.exists()) { if (!homeFolder.mkdirs()) { log.warn("Can't create drivers folder '" + homeFolder.getAbsolutePath() + "'"); } } return homeFolder; } public static String[] getDriversSources() { String sourcesString = DBeaverCore.getGlobalPreferenceStore().getString(DBeaverPreferences.UI_DRIVERS_SOURCES); List<String> pathList = CommonUtils.splitString(sourcesString, '|'); return pathList.toArray(new String[pathList.size()]); } static String getDriversPrimarySource() { String sourcesString = DBeaverCore.getGlobalPreferenceStore().getString(DBeaverPreferences.UI_DRIVERS_SOURCES); int divPos = sourcesString.indexOf('|'); return divPos == -1 ? sourcesString : sourcesString.substring(0, divPos); } @Override public String toString() { return name; } private static class ReplaceInfo { String providerId; String driverId; private ReplaceInfo(String providerId, String driverId) { this.providerId = providerId; this.driverId = driverId; } } public static class MetaURL { private List<String> urlComponents = new ArrayList<>(); private Set<String> availableProperties = new HashSet<>(); private Set<String> requiredProperties = new HashSet<>(); public List<String> getUrlComponents() { return urlComponents; } public Set<String> getAvailableProperties() { return availableProperties; } public Set<String> getRequiredProperties() { return requiredProperties; } } public static MetaURL parseSampleURL(String sampleURL) throws DBException { MetaURL metaURL = new MetaURL(); int offsetPos = 0; for (; ;) { int divPos = sampleURL.indexOf(URL_GROUP_START, offsetPos); if (divPos == -1) { break; } int divPos2 = sampleURL.indexOf(URL_GROUP_END, divPos); if (divPos2 == -1) { throw new DBException("Bad sample URL: " + sampleURL); } String propName = sampleURL.substring(divPos + 1, divPos2); boolean isOptional = false; int optDiv1 = sampleURL.lastIndexOf(URL_OPTIONAL_START, divPos); int optDiv1c = sampleURL.lastIndexOf(URL_OPTIONAL_END, divPos); int optDiv2 = sampleURL.indexOf(URL_OPTIONAL_END, divPos2); int optDiv2c = sampleURL.indexOf(URL_OPTIONAL_START, divPos2); if (optDiv1 != -1 && optDiv2 != -1 && (optDiv1c == -1 || optDiv1c < optDiv1) && (optDiv2c == -1 || optDiv2c > optDiv2)) { divPos = optDiv1; divPos2 = optDiv2; isOptional = true; } if (divPos > offsetPos) { metaURL.urlComponents.add(sampleURL.substring(offsetPos, divPos)); } metaURL.urlComponents.add(sampleURL.substring(divPos, divPos2 + 1)); metaURL.availableProperties.add(propName); if (!isOptional) { metaURL.requiredProperties.add(propName); } offsetPos = divPos2 + 1; } if (offsetPos < sampleURL.length() - 1) { metaURL.urlComponents.add(sampleURL.substring(offsetPos)); } /* // Check for required parts for (String component : urlComponents) { boolean isRequired = !component.startsWith("["); int divPos = component.indexOf('{'); if (divPos != -1) { int divPos2 = component.indexOf('}', divPos); if (divPos2 != -1) { String propName = component.substring(divPos + 1, divPos2); availableProperties.add(propName); if (isRequired) { requiredProperties.add(propName); } } } } */ return metaURL; } public static class DriversParser implements SAXListener { DataSourceProviderDescriptor curProvider; DriverDescriptor curDriver; DBPDriverLibrary curLibrary; @Override public void saxStartElement(SAXReader reader, String namespaceURI, String localName, Attributes atts) throws XMLException { switch (localName) { case RegistryConstants.TAG_PROVIDER: { curProvider = null; curDriver = null; String idAttr = atts.getValue(RegistryConstants.ATTR_ID); if (CommonUtils.isEmpty(idAttr)) { log.warn("No id for driver provider"); return; } curProvider = DataSourceProviderRegistry.getInstance().getDataSourceProvider(idAttr); if (curProvider == null) { log.warn("Datasource provider '" + idAttr + "' not found. Bad provider description."); } break; } case RegistryConstants.TAG_DRIVER: { curDriver = null; if (curProvider == null) { String providerId = atts.getValue(RegistryConstants.ATTR_PROVIDER); if (!CommonUtils.isEmpty(providerId)) { curProvider = DataSourceProviderRegistry.getInstance().getDataSourceProvider(providerId); if (curProvider == null) { log.warn("Datasource provider '" + providerId + "' not found. Bad driver description."); } } if (curProvider == null) { log.warn("Driver outside of datasource provider"); return; } } String idAttr = atts.getValue(RegistryConstants.ATTR_ID); curDriver = curProvider.getDriver(idAttr); if (curDriver == null) { curDriver = new DriverDescriptor(curProvider, idAttr); curProvider.addDriver(curDriver); } if (curProvider.isDriversManagable()) { curDriver.setCategory(atts.getValue(RegistryConstants.ATTR_CATEGORY)); curDriver.setName(atts.getValue(RegistryConstants.ATTR_NAME)); curDriver.setDescription(atts.getValue(RegistryConstants.ATTR_DESCRIPTION)); curDriver.setDriverClassName(atts.getValue(RegistryConstants.ATTR_CLASS)); curDriver.setSampleURL(atts.getValue(RegistryConstants.ATTR_URL)); curDriver.setDriverDefaultPort(atts.getValue(RegistryConstants.ATTR_PORT)); curDriver.setEmbedded(CommonUtils.getBoolean(atts.getValue(RegistryConstants.ATTR_EMBEDDED), false)); } curDriver.setCustomDriverLoader(CommonUtils.getBoolean(atts.getValue(RegistryConstants.ATTR_CUSTOM_DRIVER_LOADER), false)); curDriver.setModified(true); String disabledAttr = atts.getValue(RegistryConstants.ATTR_DISABLED); if (CommonUtils.getBoolean(disabledAttr)) { curDriver.setDisabled(true); } break; } case RegistryConstants.TAG_LIBRARY: { if (curDriver == null) { log.warn("Library outside of driver"); return; } DBPDriverLibrary.FileType type; String typeStr = atts.getValue(RegistryConstants.ATTR_TYPE); if (CommonUtils.isEmpty(typeStr)) { type = DBPDriverLibrary.FileType.jar; } else { try { type = DBPDriverLibrary.FileType.valueOf(typeStr); } catch (IllegalArgumentException e) { log.warn(e); type = DBPDriverLibrary.FileType.jar; } } String path = normalizeLibraryPath(atts.getValue(RegistryConstants.ATTR_PATH)); if (!CommonUtils.isEmpty(path)) { path = replacePathVariables(path); } boolean custom = CommonUtils.getBoolean(atts.getValue(RegistryConstants.ATTR_CUSTOM), true); String version = atts.getValue(RegistryConstants.ATTR_VERSION); DBPDriverLibrary lib = curDriver.getDriverLibrary(path); if (!custom && lib == null) { // This is predefined library from some previous version - as it wasn't defined in plugin.xml // so let's just skip it log.debug("Skip obsolete custom library '" + path + "'"); return; } String disabledAttr = atts.getValue(RegistryConstants.ATTR_DISABLED); if (lib != null && CommonUtils.getBoolean(disabledAttr)) { lib.setDisabled(true); } else if (lib == null) { lib = DriverLibraryAbstract.createFromPath(curDriver, type, path, version); curDriver.libraries.add(lib); } else if (!CommonUtils.isEmpty(version)) { lib.setPreferredVersion(version); } curLibrary = lib; break; } case RegistryConstants.TAG_FILE: { if (curDriver != null && curLibrary != null) { String path = atts.getValue(RegistryConstants.ATTR_PATH); if (path != null) { path = replacePathVariables(path); if (CommonUtils.isEmpty(path)) { log.warn("Empty path for library file"); } else { DriverFileInfo info = new DriverFileInfo( atts.getValue(CommonUtils.notEmpty(RegistryConstants.ATTR_ID)), atts.getValue(CommonUtils.notEmpty(RegistryConstants.ATTR_VERSION)), new File(path)); curDriver.addLibraryFile(curLibrary, info); } } } break; } case RegistryConstants.TAG_CLIENT_HOME: if (curDriver != null) { curDriver.addClientHomeId(atts.getValue(RegistryConstants.ATTR_ID)); } break; case RegistryConstants.TAG_PARAMETER: { if (curDriver != null) { final String paramName = atts.getValue(RegistryConstants.ATTR_NAME); final String paramValue = atts.getValue(RegistryConstants.ATTR_VALUE); if (!CommonUtils.isEmpty(paramName) && !CommonUtils.isEmpty(paramValue)) { curDriver.setDriverParameter(paramName, paramValue, false); } } break; } case RegistryConstants.TAG_PROPERTY: { if (curDriver != null) { final String paramName = atts.getValue(RegistryConstants.ATTR_NAME); final String paramValue = atts.getValue(RegistryConstants.ATTR_VALUE); if (!CommonUtils.isEmpty(paramName) && !CommonUtils.isEmpty(paramValue)) { curDriver.setConnectionProperty(paramName, paramValue); } } break; } } } // TODO: support of 3.5.1 -> 3.5.2 maven dependencies migration private static final String PATH_VERSION_OBSOLETE_RELEASE = ":release"; private static String normalizeLibraryPath(String value) { if (value.startsWith(DriverLibraryMavenArtifact.PATH_PREFIX)) { if (value.endsWith(PATH_VERSION_OBSOLETE_RELEASE)) { value = value.substring(0, value.length() - PATH_VERSION_OBSOLETE_RELEASE.length()) + ":" + MavenArtifactReference.VERSION_PATTERN_RELEASE; } } return value; } @Override public void saxText(SAXReader reader, String data) {} @Override public void saxEndElement(SAXReader reader, String namespaceURI, String localName) { switch (localName) { case RegistryConstants.TAG_LIBRARY: curLibrary = null; break; } } } private class LicenceAcceptor implements Runnable { private boolean result; private String licenseText; private LicenceAcceptor(String licenseText) { this.licenseText = licenseText; } @Override public void run() { result = AcceptLicenseDialog.acceptLicense( DBeaverUI.getActiveWorkbenchShell(), "You have to accept license of '" + getFullName() + " ' to continue", licenseText); } } private static String replacePathVariables(String path) { return GeneralUtils.replaceVariables(path, new DriverVariablesResolver()); } private static String substitutePathVariables(Map<String, String> pathSubstitutions, String path) { for (Map.Entry<String, String> ps : pathSubstitutions.entrySet()) { if (path.startsWith(ps.getKey())) { path = GeneralUtils.variablePattern(ps.getValue()) + path.substring(ps.getKey().length()); break; } } return path; } private static class DriverVariablesResolver extends SystemVariablesResolver { private static final String VAR_DRIVERS_HOME = "drivers_home"; @Override public String get(String name) { if (name.equalsIgnoreCase(VAR_DRIVERS_HOME)) { return getCustomDriversHome().getAbsolutePath(); } else { return super.get(name); } } } }