/* * 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.core.application; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.DefaultParser; import org.apache.commons.cli.HelpFormatter; import org.apache.commons.cli.Option; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.Platform; import org.eclipse.equinox.app.IApplication; import org.eclipse.equinox.app.IApplicationContext; import org.eclipse.osgi.service.datalocation.Location; import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.MessageBox; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.IWorkbench; import org.eclipse.ui.IWorkbenchPreferenceConstants; import org.eclipse.ui.PlatformUI; import org.jkiss.code.NotNull; 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.core.application.rpc.DBeaverInstanceServer; import org.jkiss.dbeaver.core.application.rpc.IInstanceController; import org.jkiss.dbeaver.core.application.rpc.InstanceClient; import org.jkiss.dbeaver.model.app.DBASecureStorage; import org.jkiss.dbeaver.model.app.DBPApplication; import org.jkiss.dbeaver.model.impl.app.DefaultSecureStorage; import org.jkiss.dbeaver.utils.GeneralUtils; import org.jkiss.dbeaver.utils.SystemVariablesResolver; import org.jkiss.utils.ArrayUtils; import org.jkiss.utils.CommonUtils; import org.jkiss.utils.IOUtils; import org.jkiss.utils.StandardConstants; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; import org.osgi.framework.BundleEvent; import org.osgi.framework.BundleListener; import java.io.*; import java.net.URL; import java.rmi.RemoteException; import java.util.*; /** * This class controls all aspects of the application's execution */ public class DBeaverApplication implements IApplication, DBPApplication { private static final Log log = Log.getLog(DBeaverApplication.class); public static final String APPLICATION_PLUGIN_ID = "org.jkiss.dbeaver.core.application"; public static final String WORKSPACE_DIR_LEGACY = ".dbeaver"; //$NON-NLS-1$ public static final String WORKSPACE_DIR_4 = ".dbeaver4"; //$NON-NLS-1$ public static final String WORKSPACE_DIR_CURRENT = WORKSPACE_DIR_4; public static final String WORKSPACE_DIR_PREVIOUS[] = { WORKSPACE_DIR_LEGACY }; public static final String WORKSPACE_PROPS_FILE = "dbeaver-workspace.properties"; //$NON-NLS-1$ static final String VERSION_PROP_PRODUCT_NAME = "product-name"; static final String VERSION_PROP_PRODUCT_VERSION = "product-version"; static boolean WORKSPACE_MIGRATED = false; private static DBeaverApplication instance; private IInstanceController instanceServer; private OutputStream debugWriter; private PrintStream oldSystemOut; private PrintStream oldSystemErr; private Display display = null; static { // Explicitly set UTF-8 as default file encoding // In some places Eclipse reads this property directly. //System.setProperty(StandardConstants.ENV_FILE_ENCODING, GeneralUtils.UTF8_ENCODING); } /** * Gets singleton instance of DBeaver application * @return application or null if application wasn't started or was stopped. */ public static DBeaverApplication getInstance() { return instance; } @Override public Object start(IApplicationContext context) { instance = this; // Set display name at the very beginning (#609) // This doesn't initialize display - just sets default title Display.setAppName(GeneralUtils.getProductName()); Location instanceLoc = Platform.getInstanceLocation(); if (!instanceLoc.isSet()) { if (!setDefaultWorkspacePath(instanceLoc)) { return IApplication.EXIT_OK; } } // Add bundle load logger Bundle brandingBundle = context.getBrandingBundle(); if (brandingBundle != null) { BundleContext bundleContext = brandingBundle.getBundleContext(); if (bundleContext != null) { bundleContext.addBundleListener(new BundleLoadListener()); } } Log.addListener(new Log.Listener() { @Override public void loggedMessage(Object message, Throwable t) { DBeaverSplashHandler.showMessage(CommonUtils.toString(message)); } }); final Runtime runtime = Runtime.getRuntime(); // Init Core plugin and mark it as standalone version DBeaverCore.setApplication(this); initDebugWriter(); log.debug(GeneralUtils.getProductTitle() + " is starting"); //$NON-NLS-1$ log.debug("Install path: '" + SystemVariablesResolver.getInstallPath() + "'"); //$NON-NLS-1$ //$NON-NLS-2$ log.debug("Instance path: '" + instanceLoc.getURL() + "'"); //$NON-NLS-1$ //$NON-NLS-2$ log.debug("Memory available " + (runtime.totalMemory() / (1024 * 1024)) + "Mb/" + (runtime.maxMemory() / (1024 * 1024)) + "Mb"); // Write version info writeWorkspaceInfo(); // Run instance server instanceServer = DBeaverInstanceServer.startInstanceServer(); // Set default resource encoding to UTF-8 String defEncoding = DBeaverCore.getGlobalPreferenceStore().getString(DBeaverPreferences.DEFAULT_RESOURCE_ENCODING); if (CommonUtils.isEmpty(defEncoding)) { defEncoding = GeneralUtils.UTF8_ENCODING; } ResourcesPlugin.getPlugin().getPluginPreferences().setValue(ResourcesPlugin.PREF_ENCODING, defEncoding); // Create display getDisplay(); // Prefs default PlatformUI.getPreferenceStore().setDefault( IWorkbenchPreferenceConstants.KEY_CONFIGURATION_ID, ApplicationWorkbenchAdvisor.DBEAVER_SCHEME_NAME); try { log.debug("Run workbench"); int returnCode = PlatformUI.createAndRunWorkbench(display, createWorkbenchAdvisor()); if (returnCode == PlatformUI.RETURN_RESTART) { return IApplication.EXIT_RESTART; } return IApplication.EXIT_OK; } finally { /* try { Job.getJobManager().join(null, new NullProgressMonitor()); } catch (InterruptedException e) { e.printStackTrace(); } */ display.dispose(); display = null; } } private Display getDisplay() { if (display == null) { log.debug("Create display"); display = PlatformUI.createDisplay(); } return display; } private boolean setDefaultWorkspacePath(Location instanceLoc) { String defaultHomePath = getDefaultWorkspaceLocation(WORKSPACE_DIR_CURRENT).getAbsolutePath(); final File homeDir = new File(defaultHomePath); if (!homeDir.exists()) { File previousVersionWorkspaceDir = null; for (String oldDir : WORKSPACE_DIR_PREVIOUS) { final File oldWorkspaceDir = new File(getDefaultWorkspaceLocation(oldDir).getAbsolutePath()); if (oldWorkspaceDir.exists() && GeneralUtils.getMetadataFolder(oldWorkspaceDir).exists()) { previousVersionWorkspaceDir = oldWorkspaceDir; break; } } if (previousVersionWorkspaceDir != null) { DBeaverSettingsImporter importer = new DBeaverSettingsImporter(this, getDisplay()); if (!importer.migrateFromPreviousVersion(previousVersionWorkspaceDir, homeDir)) { return false; } } } if (handleCommandLine(defaultHomePath)) { return false; } try { // Make URL manually because file.toURI().toURL() produces bad path (with %20). final URL defaultHomeURL = new URL( "file", //$NON-NLS-1$ null, defaultHomePath); boolean keepTrying = true; while (keepTrying) { if (!instanceLoc.set(defaultHomeURL, true)) { // Can't lock specified path int msgResult = showMessageBox( "DBeaver - Can't lock workspace", "Can't lock workspace at " + defaultHomePath + ".\n" + "It seems that you have another DBeaver instance running.\n" + "You may ignore it and work without lock but it is recommended to shutdown previous instance otherwise you may corrupt workspace data.", SWT.ICON_WARNING | SWT.IGNORE | SWT.RETRY | SWT.ABORT); switch (msgResult) { case SWT.ABORT: return false; case SWT.IGNORE: instanceLoc.set(defaultHomeURL, false); keepTrying = false; break; case SWT.RETRY: break; } } else { break; } } } catch (Throwable e) { // Just skip it // Error may occur if -data parameter was specified at startup System.err.println("Can't switch workspace to '" + defaultHomePath + "' - " + e.getMessage()); //$NON-NLS-1$ //$NON-NLS-2$ } return true; } private void writeWorkspaceInfo() { final File metadataFolder = GeneralUtils.getMetadataFolder(); writeWorkspaceInfo(metadataFolder); } private void writeWorkspaceInfo(File metadataFolder) { File versionFile = new File(metadataFolder, WORKSPACE_PROPS_FILE); Properties props = new Properties(); props.setProperty(VERSION_PROP_PRODUCT_NAME, GeneralUtils.getProductName()); props.setProperty(VERSION_PROP_PRODUCT_VERSION, GeneralUtils.getProductVersion().toString()); try (OutputStream os = new FileOutputStream(versionFile)) { props.store(os, "DBeaver workspace version"); } catch (Exception e) { log.error(e); } } Properties readWorkspaceInfo(File metadataFolder) { Properties props = new Properties(); File versionFile = new File(metadataFolder, WORKSPACE_PROPS_FILE); if (versionFile.exists()) { try (InputStream is = new FileInputStream(versionFile)) { props.load(is); } catch (Exception e) { log.error(e); } } return props; } @NotNull protected ApplicationWorkbenchAdvisor createWorkbenchAdvisor() { return new ApplicationWorkbenchAdvisor(); } private boolean handleCommandLine(String instanceLoc) { CommandLine commandLine = getCommandLine(); if (commandLine == null || (ArrayUtils.isEmpty(commandLine.getArgs()) && ArrayUtils.isEmpty(commandLine.getOptions()))) { return false; } if (commandLine.hasOption(DBeaverCommandLine.PARAM_HELP)) { HelpFormatter helpFormatter = new HelpFormatter(); helpFormatter.setWidth(120); helpFormatter.setOptionComparator(new Comparator<Option>() { @Override public int compare(Option o1, Option o2) { return 0; } }); helpFormatter.printHelp("dbeaver", GeneralUtils.getProductTitle(), DBeaverCommandLine.ALL_OPTIONS, "(C) 2017 JKISS", true); return true; } try { IInstanceController controller = InstanceClient.createClient(instanceLoc); if (controller == null) { return false; } return executeCommandLineCommands(commandLine, controller); } catch (RemoteException e) { log.error("Error calling remote server", e); return true; } catch (Throwable e) { log.error("Error while calling remote server", e); return true; } } @Override public void stop() { log.debug("DBeaver is stopping"); //$NON-NLS-1$ try { final IWorkbench workbench = PlatformUI.getWorkbench(); if (workbench == null) return; instanceServer = null; DBeaverInstanceServer.stopInstanceServer(); final Display display = workbench.getDisplay(); DBeaverUI.syncExec(new Runnable() { @Override public void run() { if (!display.isDisposed()) workbench.close(); } }); } catch (Throwable e) { log.error(e); } finally { instance = null; stopDebugWriter(); } } private void initDebugWriter() { File logPath = GeneralUtils.getMetadataFolder(); File debugLogFile = new File(logPath, "dbeaver-debug.log"); //$NON-NLS-1$ if (debugLogFile.exists()) { if (!debugLogFile.delete()) { System.err.println("Can't delete debug log file"); //$NON-NLS-1$ } } try { debugWriter = new FileOutputStream(debugLogFile); oldSystemOut = System.out; oldSystemErr = System.err; System.setOut(new PrintStream(new ProxyPrintStream(debugWriter, oldSystemOut))); System.setErr(new PrintStream(new ProxyPrintStream(debugWriter, oldSystemErr))); } catch (IOException e) { e.printStackTrace(System.err); } } private void stopDebugWriter() { if (oldSystemOut != null) System.setOut(oldSystemOut); if (oldSystemErr != null) System.setErr(oldSystemErr); if (debugWriter != null) { IOUtils.close(debugWriter); debugWriter = null; } } public static boolean executeCommandLineCommands(CommandLine commandLine, IInstanceController controller) throws Exception { if (commandLine == null) { return false; } { // Open files String[] files = commandLine.getOptionValues(DBeaverCommandLine.PARAM_FILE); String[] fileArgs = commandLine.getArgs(); if (!ArrayUtils.isEmpty(files) || !ArrayUtils.isEmpty(fileArgs)) { List<String> fileNames = new ArrayList<>(); if (!ArrayUtils.isEmpty(files)) { Collections.addAll(fileNames, files); } if (!ArrayUtils.isEmpty(fileArgs)) { Collections.addAll(fileNames, fileArgs); } controller.openExternalFiles(fileNames.toArray(new String[fileNames.size()])); return true; } } { // Connect String[] connectParams = commandLine.getOptionValues(DBeaverCommandLine.PARAM_CONNECT); if (!ArrayUtils.isEmpty(connectParams)) { for (String cp : connectParams) { controller.openDatabaseConnection(cp); } return true; } } if (commandLine.hasOption(DBeaverCommandLine.PARAM_STOP)) { controller.quit(); return true; } if (commandLine.hasOption(DBeaverCommandLine.PARAM_THREAD_DUMP)) { String threadDump = controller.getThreadDump(); System.out.println(threadDump); return true; } return false; } public IInstanceController getInstanceServer() { return instanceServer; } private static File getDefaultWorkspaceLocation(String path) { return new File( System.getProperty(StandardConstants.ENV_USER_HOME), path); } public static CommandLine getCommandLine() { try { return new DefaultParser().parse(DBeaverCommandLine.ALL_OPTIONS, Platform.getApplicationArgs(), false); } catch (Exception e) { log.error("Error parsing command line: " + e.getMessage()); return null; } } @Override public boolean isStandalone() { return true; } @NotNull @Override public DBASecureStorage getSecureStorage() { return DefaultSecureStorage.INSTANCE; } int showMessageBox(String title, String message, int style) { // Can't lock specified path Shell shell = new Shell(getDisplay(), SWT.ON_TOP); shell.setText(GeneralUtils.getProductTitle()); MessageBox messageBox = new MessageBox(shell, style); messageBox.setText(title); messageBox.setMessage(message); int msgResult = messageBox.open(); shell.dispose(); return msgResult; } private static class BundleLoadListener implements BundleListener { @Override public void bundleChanged(BundleEvent event) { String message = null; if (event.getType() == BundleEvent.STARTED) { message = "> Start " + event.getBundle().getSymbolicName() + " [" + event.getBundle().getVersion() + "]"; } else if (event.getType() == BundleEvent.STOPPED) { message = "< Stop " + event.getBundle().getSymbolicName() + " [" + event.getBundle().getVersion() + "]"; } if (message != null) { log.debug(message); } } } private class ProxyPrintStream extends OutputStream { private final OutputStream debugWriter; private final OutputStream stdOut; public ProxyPrintStream(OutputStream debugWriter, OutputStream stdOut) { this.debugWriter = debugWriter; this.stdOut = stdOut; } @Override public void write(int b) throws IOException { debugWriter.write(b); stdOut.write(b); } } }