/* * 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.model.sql.format.external; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.jkiss.dbeaver.Log; import org.jkiss.dbeaver.ModelPreferences; import org.jkiss.dbeaver.model.preferences.DBPPreferenceStore; import org.jkiss.dbeaver.model.runtime.AbstractJob; import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor; import org.jkiss.dbeaver.model.sql.format.SQLFormatter; import org.jkiss.dbeaver.model.sql.format.SQLFormatterConfiguration; import org.jkiss.dbeaver.utils.GeneralUtils; import org.jkiss.utils.ArgumentTokenizer; import org.jkiss.utils.CommonUtils; import org.jkiss.utils.IOUtils; import java.io.*; import java.util.List; /** * External SQL formatter */ public class SQLExternalFormatter implements SQLFormatter { public static final String FORMATTER_ID = "EXTERNAL"; private static final Log log = Log.getLog(SQLExternalFormatter.class); public static final String VAR_FILE = "file"; @Override public String format(String source, SQLFormatterConfiguration configuration) { final DBPPreferenceStore store = configuration.getSyntaxManager().getPreferenceStore(); final String command = store.getString(ModelPreferences.SQL_FORMAT_EXTERNAL_CMD); int timeout = store.getInt(ModelPreferences.SQL_FORMAT_EXTERNAL_TIMEOUT); boolean useFile = store.getBoolean(ModelPreferences.SQL_FORMAT_EXTERNAL_FILE); try { final FormatJob formatJob = new FormatJob(configuration, command, source, useFile); formatJob.schedule(); for (int i = 0; i < 10; i++) { Thread.sleep(timeout / 10); if (formatJob.finished) { return formatJob.result; } } log.warn("Formatter process hangs. Terminating."); formatJob.stop(); } catch (Exception ex) { log.warn("Error executing external formatter [" + command + "]", ex); } return source; } private static class FormatJob extends AbstractJob { SQLFormatterConfiguration configuration; String command; Process process; String source; boolean useFile; String result = ""; public boolean finished; public FormatJob(SQLFormatterConfiguration configuration, String command, String source, boolean useFile) { super("External format: " + command); this.command = command; this.configuration = configuration; this.source = source; this.useFile = useFile; } @Override protected IStatus run(DBRProgressMonitor monitor) { final String sourceEncoding = configuration.getSourceEncoding(); File tmpFile = null; try { if (CommonUtils.isEmpty(command)) { throw new IOException("No command specified for external formatter"); } if (useFile) { tmpFile = File.createTempFile("dbeaver-sql-format", "sql"); try (final OutputStream os = new FileOutputStream(tmpFile)) { try (final Writer out = new OutputStreamWriter(os, sourceEncoding)) { IOUtils.copyText(new StringReader(source), out); } } command = command.replace(GeneralUtils.variablePattern(VAR_FILE), tmpFile.getAbsolutePath()); } List<String> commandList = ArgumentTokenizer.tokenize(command, false); ProcessBuilder pb = new ProcessBuilder(commandList); pb.redirectErrorStream(true); process = pb.start(); try { if (tmpFile == null) { try (final OutputStream stdout = process.getOutputStream()) { stdout.write(source.getBytes(sourceEncoding)); } } StringWriter buf = new StringWriter(); try (Reader input = new InputStreamReader(process.getInputStream(), sourceEncoding)) { IOUtils.copyText(input, buf); } int rc = process.waitFor(); if (rc != 0) { log.debug("Formatter result code: " + rc); } result = buf.toString(); } finally { process.destroy(); } } catch (Exception e) { result = source; finished = true; return GeneralUtils.makeExceptionStatus(e); // log.error(e); } finally { if (tmpFile != null && tmpFile.exists()) { if (!tmpFile.delete()) { log.debug("Can't delete temp file '" + tmpFile.getAbsolutePath() + "'"); } } } finished = true; return Status.OK_STATUS; } void stop() { if (process != null) { process.destroy(); } } } }