/* * DBeaver - Universal Database Manager * Copyright (C) 2010-2017 Serge Rider (serge@jkiss.org) * Copyright (C) 2011-2012 Eugene Fradkin (eugene.fradkin@gmail.com) * * 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.ui.dialogs.tools; import org.eclipse.core.runtime.CoreException; import org.eclipse.jface.wizard.Wizard; import org.eclipse.jface.wizard.WizardPage; import org.eclipse.osgi.util.NLS; import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.Composite; import org.jkiss.dbeaver.Log; import org.jkiss.dbeaver.core.CoreMessages; import org.jkiss.dbeaver.core.DBeaverUI; import org.jkiss.dbeaver.model.DBPDataSourceContainer; import org.jkiss.dbeaver.model.connection.DBPClientHome; import org.jkiss.dbeaver.model.connection.DBPConnectionConfiguration; import org.jkiss.dbeaver.model.navigator.DBNDatabaseNode; import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor; import org.jkiss.dbeaver.model.runtime.DBRRunnableWithProgress; import org.jkiss.dbeaver.model.struct.DBSObject; import org.jkiss.dbeaver.ui.UIUtils; import org.jkiss.utils.CommonUtils; import org.jkiss.utils.IOUtils; import java.io.*; import java.lang.reflect.InvocationTargetException; import java.text.NumberFormat; import java.util.ArrayList; import java.util.Collection; import java.util.List; /** * Abstract wizard */ public abstract class AbstractToolWizard<BASE_OBJECT extends DBSObject, PROCESS_ARG> extends Wizard implements DBRRunnableWithProgress { private static final Log log = Log.getLog(AbstractToolWizard.class); private final List<BASE_OBJECT> databaseObjects; private DBPClientHome clientHome; private DBPDataSourceContainer dataSourceContainer; private DBPConnectionConfiguration connectionInfo; private String toolUserName; private String toolUserPassword; protected String task; protected final DatabaseWizardPageLog logPage; private boolean finished; protected AbstractToolWizard(Collection<BASE_OBJECT> databaseObjects, String task) { this.databaseObjects = new ArrayList<>(databaseObjects); this.task = task; this.logPage = new DatabaseWizardPageLog(task); if (databaseObjects.isEmpty()) { throw new IllegalArgumentException("Empty object list"); } for (BASE_OBJECT object : databaseObjects) { if (dataSourceContainer != null && dataSourceContainer != object.getDataSource().getContainer()) { throw new IllegalArgumentException("Objects from different data sources"); } dataSourceContainer = object.getDataSource().getContainer(); connectionInfo = dataSourceContainer.getActualConnectionConfiguration(); } } @Override public boolean canFinish() { return !finished && super.canFinish(); } public List<BASE_OBJECT> getDatabaseObjects() { return databaseObjects; } public DBPConnectionConfiguration getConnectionInfo() { return connectionInfo; } public DBPClientHome getClientHome() { return clientHome; } public String getToolUserName() { return toolUserName; } public void setToolUserName(String toolUserName) { this.toolUserName = toolUserName; } public String getToolUserPassword() { return toolUserPassword; } public void setToolUserPassword(String toolUserPassword) { this.toolUserPassword = toolUserPassword; } public abstract DBPClientHome findServerHome(String clientHomeId); public abstract Collection<PROCESS_ARG> getRunInfo(); @Override public void createPageControls(Composite pageContainer) { super.createPageControls(pageContainer); WizardPage currentPage = (WizardPage) getStartingPage(); String clientHomeId = connectionInfo.getClientHomeId(); if (clientHomeId == null) { currentPage.setErrorMessage(CoreMessages.tools_wizard_message_no_client_home); getContainer().updateMessage(); return; } clientHome = findServerHome(clientHomeId);//MySQLDataSourceProvider.getServerHome(clientHomeId); if (clientHome == null) { currentPage.setErrorMessage(NLS.bind(CoreMessages.tools_wizard_message_client_home_not_found, clientHomeId)); getContainer().updateMessage(); } } @Override public boolean performFinish() { if (getContainer().getCurrentPage() != logPage) { getContainer().showPage(logPage); } try { DBeaverUI.run(getContainer(), true, true, this); } catch (InterruptedException ex) { UIUtils.showMessageBox(getShell(), task, NLS.bind(CoreMessages.tools_wizard_error_task_canceled, task, getObjectsName()), SWT.ICON_ERROR); return false; } catch (InvocationTargetException ex) { UIUtils.showErrorDialog( getShell(), NLS.bind(CoreMessages.tools_wizard_error_task_error_title, task), CoreMessages.tools_wizard_error_task_error_message + task, ex.getTargetException()); return false; } finally { getContainer().updateButtons(); } onSuccess(); return false; } public String getObjectsName() { StringBuilder str = new StringBuilder(); for (BASE_OBJECT object : databaseObjects) { if (str.length() > 0) str.append(","); str.append(object.getName()); } return str.toString(); } @Override public void run(DBRProgressMonitor monitor) throws InvocationTargetException, InterruptedException { try { for (PROCESS_ARG arg : getRunInfo()) { if (monitor.isCanceled()) break; executeProcess(monitor, arg); } if (!monitor.isCanceled()) { // Refresh navigator node (script execution can change everything inside) for (BASE_OBJECT object : databaseObjects) { final DBNDatabaseNode node = dataSourceContainer.getPlatform().getNavigatorModel().findNode(object); if (node != null) { node.refreshNode(monitor, AbstractToolWizard.this); } } } } catch (InterruptedException e) { throw e; } catch (Exception e) { throw new InvocationTargetException(e); } finally { finished = true; } if (monitor.isCanceled()) { throw new InterruptedException(); } } public boolean executeProcess(DBRProgressMonitor monitor, PROCESS_ARG arg) throws IOException, CoreException, InterruptedException { try { final List<String> commandLine = getCommandLine(arg); final File execPath = new File(commandLine.get(0)); ProcessBuilder processBuilder = new ProcessBuilder(commandLine); processBuilder.directory(execPath.getParentFile()); if (this.isMergeProcessStreams()) { processBuilder.redirectErrorStream(true); } setupProcessParameters(processBuilder); Process process = processBuilder.start(); startProcessHandler(monitor, arg, processBuilder, process); Thread.sleep(100); for (;;) { Thread.sleep(100); if (monitor.isCanceled()) { process.destroy(); } try { final int exitCode = process.exitValue(); if (exitCode != 0) { logPage.appendLog(NLS.bind(CoreMessages.tools_wizard_log_process_exit_code, exitCode) + "\n", true); return false; } } catch (IllegalThreadStateException e) { // Still running continue; } break; } //process.waitFor(); } catch (IOException e) { log.error(e); logPage.appendLog(NLS.bind(CoreMessages.tools_wizard_log_io_error, e.getMessage()) + "\n", true); return false; } return true; } protected boolean isMergeProcessStreams() { return false; } public boolean isVerbose() { return false; } protected void onSuccess() { } abstract protected java.util.List<String> getCommandLine(PROCESS_ARG arg) throws IOException; public abstract void fillProcessParameters(List<String> cmd, PROCESS_ARG arg) throws IOException; protected void setupProcessParameters(ProcessBuilder process) { } protected abstract void startProcessHandler(DBRProgressMonitor monitor, PROCESS_ARG arg, ProcessBuilder processBuilder, Process process); public boolean isSecureString(String string) { String password = getToolUserPassword(); return !CommonUtils.isEmpty(password) && string.contains(password); } public abstract class DumpJob extends Thread { protected DBRProgressMonitor monitor; protected InputStream input; protected File outFile; protected DumpJob(String name, DBRProgressMonitor monitor, InputStream stream, File outFile) { super(name); this.monitor = monitor; this.input = stream; this.outFile = outFile; } @Override public final void run() { try { runDump(); } catch (IOException e) { logPage.appendLog(e.getMessage()); } } protected abstract void runDump() throws IOException; } public class DumpCopierJob extends DumpJob { public DumpCopierJob(DBRProgressMonitor monitor, String name, InputStream stream, File outFile) { super(name, monitor, stream, outFile); } @Override public void runDump() throws IOException { monitor.beginTask(getName(), 100); long totalBytesDumped = 0; long prevStatusUpdateTime = 0; byte[] buffer = new byte[10000]; try { NumberFormat numberFormat = NumberFormat.getInstance(); try (OutputStream output = new FileOutputStream(outFile)){ for (;;) { int count = input.read(buffer); if (count <= 0) { break; } totalBytesDumped += count; long currentTime = System.currentTimeMillis(); if (currentTime - prevStatusUpdateTime > 300) { monitor.subTask(numberFormat.format(totalBytesDumped) + " bytes"); prevStatusUpdateTime = currentTime; } output.write(buffer, 0, count); } output.flush(); } } finally { monitor.done(); } } } public class TextFileTransformerJob extends Thread { private DBRProgressMonitor monitor; private OutputStream output; private File inputFile; private String inputCharset; private String outputCharset; public TextFileTransformerJob(DBRProgressMonitor monitor, File inputFile, OutputStream stream, String inputCharset, String outputCharset) { super(task); this.monitor = monitor; this.output = stream; this.inputFile = inputFile; this.inputCharset = inputCharset; this.outputCharset = outputCharset; } @Override public void run() { try { try (InputStream scriptStream = new ProgressStreamReader( monitor, new FileInputStream(inputFile), inputFile.length())) { BufferedReader reader = new BufferedReader(new InputStreamReader(scriptStream, inputCharset)); PrintWriter writer = new PrintWriter(new OutputStreamWriter(output, outputCharset)); while (!monitor.isCanceled()) { String line = reader.readLine(); if (line == null) { break; } writer.println(line); writer.flush(); } output.flush(); } finally { IOUtils.close(output); } } catch (IOException e) { log.debug(e); logPage.appendLog(e.getMessage()); } finally { monitor.done(); } } } public class BinaryFileTransformerJob extends Thread { private DBRProgressMonitor monitor; private OutputStream output; private File inputFile; public BinaryFileTransformerJob(DBRProgressMonitor monitor, File inputFile, OutputStream stream) { super(task); this.monitor = monitor; this.output = stream; this.inputFile = inputFile; } @Override public void run() { try (InputStream scriptStream = new ProgressStreamReader( monitor, new FileInputStream(inputFile), inputFile.length())) { byte[] buffer = new byte[100000]; while (!monitor.isCanceled()) { int readSize = scriptStream.read(buffer); if (readSize < 0) { break; } output.write(buffer, 0, readSize); output.flush(); } output.flush(); } catch (IOException e) { log.debug(e); logPage.appendLog(e.getMessage() + "\n"); } finally { monitor.done(); } } } private class ProgressStreamReader extends InputStream { static final int BUFFER_SIZE = 10000; private final DBRProgressMonitor monitor; private final InputStream original; private final long streamLength; private long totalRead; private ProgressStreamReader(DBRProgressMonitor monitor, InputStream original, long streamLength) { this.monitor = monitor; this.original = original; this.streamLength = streamLength; this.totalRead = 0; monitor.beginTask(task, (int)streamLength); } @Override public int read() throws IOException { int res = original.read(); showProgress(res); return res; } @Override public int read(byte[] b) throws IOException { int res = original.read(b); showProgress(res); return res; } @Override public int read(byte[] b, int off, int len) throws IOException { int res = original.read(b, off, len); showProgress(res); return res; } @Override public long skip(long n) throws IOException { long res = original.skip(n); showProgress(res); return res; } @Override public int available() throws IOException { return original.available(); } @Override public void close() throws IOException { monitor.done(); original.close(); } private void showProgress(long length) { totalRead += length; monitor.worked((int)length); } } }