franta-hg@16: /** franta-hg@16: * SQL-DK franta-hg@16: * Copyright © 2013 František Kučera (frantovo.cz) franta-hg@16: * franta-hg@16: * This program is free software: you can redistribute it and/or modify franta-hg@16: * it under the terms of the GNU General Public License as published by franta-hg@16: * the Free Software Foundation, either version 3 of the License, or franta-hg@16: * (at your option) any later version. franta-hg@16: * franta-hg@16: * This program is distributed in the hope that it will be useful, franta-hg@16: * but WITHOUT ANY WARRANTY; without even the implied warranty of franta-hg@16: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the franta-hg@16: * GNU General Public License for more details. franta-hg@16: * franta-hg@16: * You should have received a copy of the GNU General Public License franta-hg@16: * along with this program. If not, see . franta-hg@16: */ franta-hg@1: package info.globalcode.sql.dk; franta-hg@1: franta-hg@26: import info.globalcode.sql.dk.configuration.ConfigurationProvider; franta-hg@20: import info.globalcode.sql.dk.CLIOptions.MODE; franta-hg@26: import info.globalcode.sql.dk.configuration.Configuration; franta-hg@33: import info.globalcode.sql.dk.configuration.ConfigurationException; franta-hg@34: import info.globalcode.sql.dk.configuration.DatabaseDefinition; franta-hg@34: import info.globalcode.sql.dk.configuration.FormatterDefinition; franta-hg@80: import info.globalcode.sql.dk.configuration.NameIdentified; franta-hg@34: import info.globalcode.sql.dk.formatting.Formatter; franta-hg@34: import info.globalcode.sql.dk.formatting.FormatterContext; franta-hg@34: import info.globalcode.sql.dk.formatting.FormatterException; franta-hg@80: import java.io.File; franta-hg@80: import java.io.FileNotFoundException; franta-hg@33: import java.io.IOException; franta-hg@64: import java.io.PrintStream; franta-hg@80: import java.io.PrintWriter; franta-hg@34: import java.sql.SQLException; franta-hg@80: import java.util.Collection; franta-hg@13: import java.util.logging.Level; franta-hg@63: import java.util.logging.LogRecord; franta-hg@13: import java.util.logging.Logger; franta-hg@33: import javax.xml.bind.JAXBContext; franta-hg@33: import javax.xml.bind.Unmarshaller; franta-hg@13: franta-hg@1: /** franta-hg@1: * franta-hg@1: * @author Ing. František Kučera (frantovo.cz) franta-hg@1: */ franta-hg@20: public class CLIStarter implements ConfigurationProvider { franta-hg@1: franta-hg@96: // help:exit-codes franta-hg@95: public static final int EXIT_SUCCESS = 0; // doc:success franta-hg@95: public static final int EXIT_UNEXPECTED_ERROR = 1; // doc:unexpected error (probably bug) franta-hg@95: public static final int EXIT_SQL_ERROR = 3; // doc:SQL error franta-hg@95: public static final int EXIT_CLI_PARSE_ERROR = 4; // doc:CLI options parse error franta-hg@95: public static final int EXIT_CLI_VALIDATE_ERROR = 5; // doc:CLI options validation error franta-hg@95: public static final int EXIT_CONFIGURATION_ERROR = 6; // doc:configuration error franta-hg@95: public static final int EXIT_FORMATTING_ERROR = 7; // doc:formatting error franta-hg@13: private static final Logger log = Logger.getLogger(CLIStarter.class.getName()); franta-hg@20: private CLIOptions options; franta-hg@20: private Configuration configuration; franta-hg@13: franta-hg@1: public static void main(String[] args) { franta-hg@55: log.log(Level.FINE, "Starting " + Constants.PROGRAM_NAME); franta-hg@95: int exitCode; franta-hg@63: franta-hg@21: if (args.length == 0) { franta-hg@21: args = new String[]{CLIParser.Tokens.INFO_HELP}; franta-hg@21: } franta-hg@21: franta-hg@13: try { franta-hg@13: CLIParser parser = new CLIParser(); franta-hg@13: CLIOptions options = parser.parseOptions(args); franta-hg@14: options.validate(); franta-hg@20: CLIStarter starter = new CLIStarter(options); franta-hg@21: starter.installDefaultConfiguration(); franta-hg@20: starter.process(); franta-hg@55: log.log(Level.FINE, "All done"); franta-hg@56: exitCode = EXIT_SUCCESS; franta-hg@13: } catch (CLIParserException e) { franta-hg@14: log.log(Level.SEVERE, "Unable to parse CLI options", e); franta-hg@95: exitCode = EXIT_CLI_PARSE_ERROR; franta-hg@14: } catch (InvalidOptionsException e) { franta-hg@14: log.log(Level.SEVERE, "Invalid CLI options", e); franta-hg@48: for (InvalidOptionsException.OptionProblem p : e.getProblems()) { franta-hg@63: LogRecord r = new LogRecord(Level.SEVERE, "Option problem: {0}"); franta-hg@63: r.setThrown(p.getException()); franta-hg@63: r.setParameters(new Object[]{p.getDescription()}); franta-hg@63: log.log(r); franta-hg@48: } franta-hg@95: exitCode = EXIT_CLI_VALIDATE_ERROR; franta-hg@34: } catch (ConfigurationException e) { franta-hg@34: log.log(Level.SEVERE, "Configuration problem", e); franta-hg@95: exitCode = EXIT_CONFIGURATION_ERROR; franta-hg@34: } catch (SQLException e) { franta-hg@34: log.log(Level.SEVERE, "SQL problem", e); franta-hg@56: exitCode = EXIT_SQL_ERROR; franta-hg@34: } catch (FormatterException e) { franta-hg@34: log.log(Level.SEVERE, "Formatting problem", e); franta-hg@95: exitCode = EXIT_FORMATTING_ERROR; franta-hg@13: } franta-hg@56: franta-hg@56: System.exit(exitCode); franta-hg@4: } franta-hg@20: franta-hg@20: public CLIStarter(CLIOptions options) { franta-hg@20: this.options = options; franta-hg@20: } franta-hg@20: franta-hg@34: private void process() throws ConfigurationException, SQLException, FormatterException { franta-hg@64: MODE mode = options.getMode(); franta-hg@64: franta-hg@20: /** Show info */ franta-hg@20: if (!options.getShowInfo().isEmpty()) { franta-hg@64: PrintStream infoOut = mode == MODE.JUST_SHOW_INFO ? System.out : System.err; franta-hg@69: InfoLister infoLister = new InfoLister(infoOut, this, options); franta-hg@70: infoLister.showInfo(); franta-hg@20: } franta-hg@20: franta-hg@20: switch (mode) { franta-hg@20: case QUERY_NOW: franta-hg@34: processQueryNow(); franta-hg@20: break; franta-hg@20: case PREPARE_BATCH: franta-hg@34: processPrepareBatch(); franta-hg@20: break; franta-hg@20: case EXECUTE_BATCH: franta-hg@34: processExecuteBatch(); franta-hg@20: break; franta-hg@20: case JUST_SHOW_INFO: franta-hg@20: // already done above franta-hg@20: break; franta-hg@20: default: franta-hg@20: log.log(Level.SEVERE, "Unsupported mode: {0}", mode); franta-hg@20: break; franta-hg@20: } franta-hg@80: franta-hg@80: generateBashCompletion(); franta-hg@20: } franta-hg@20: franta-hg@34: private void processQueryNow() throws ConfigurationException, SQLException, FormatterException { franta-hg@34: DatabaseDefinition dd = getConfiguration().getDatabase(options.getDatabaseName()); franta-hg@75: FormatterDefinition fd = configuration.getFormatter(options.getFormatterName()); franta-hg@75: try (DatabaseConnection c = dd.connect()) { franta-hg@75: log.log(Level.FINE, "Database connected"); franta-hg@104: try (Formatter f = fd.getInstance(new FormatterContext(options.getOutputStream(), options.getFormatterProperties()))) { franta-hg@101: c.executeQuery(options.getSQLCommand(), f); franta-hg@101: } franta-hg@34: } franta-hg@34: } franta-hg@34: franta-hg@34: private void processPrepareBatch() { franta-hg@34: } franta-hg@34: franta-hg@34: private void processExecuteBatch() { franta-hg@34: } franta-hg@34: franta-hg@20: @Override franta-hg@33: public Configuration getConfiguration() throws ConfigurationException { franta-hg@20: if (configuration == null) { franta-hg@20: configuration = loadConfiguration(); franta-hg@20: } franta-hg@20: return configuration; franta-hg@20: } franta-hg@20: franta-hg@34: private void installDefaultConfiguration() throws ConfigurationException { franta-hg@33: Constants.DIR.mkdir(); franta-hg@33: franta-hg@33: if (Constants.CONFIG_FILE.exists()) { franta-hg@55: log.log(Level.FINER, "Config file already exists: {0}", Constants.CONFIG_FILE); franta-hg@33: } else { franta-hg@33: try { franta-hg@33: Functions.installResource(Constants.EXAMPLE_CONFIG_FILE, Constants.CONFIG_FILE); franta-hg@55: log.log(Level.FINE, "Installing default config file: {0}", Constants.CONFIG_FILE); franta-hg@33: } catch (IOException e) { franta-hg@34: throw new ConfigurationException("Unable to write example configuration to " + Constants.CONFIG_FILE, e); franta-hg@33: } franta-hg@33: } franta-hg@20: } franta-hg@20: franta-hg@33: private Configuration loadConfiguration() throws ConfigurationException { franta-hg@33: try { franta-hg@33: JAXBContext jaxb = JAXBContext.newInstance(Configuration.class); franta-hg@33: Unmarshaller u = jaxb.createUnmarshaller(); franta-hg@33: return (Configuration) u.unmarshal(Constants.CONFIG_FILE); franta-hg@33: } catch (Exception e) { franta-hg@33: throw new ConfigurationException("Unable to load configuration from " + Constants.CONFIG_FILE, e); franta-hg@33: } franta-hg@20: } franta-hg@80: franta-hg@80: private void generateBashCompletion() { franta-hg@80: if (configuration == null) { franta-hg@80: log.log(Level.FINER, "Not writing Bash completion helper files. In order to generate these files please run some command which requires configuration."); franta-hg@80: } else { franta-hg@80: try { franta-hg@80: File dir = new File(Constants.DIR, "bash-completion"); franta-hg@80: dir.mkdir(); franta-hg@80: writeBashCompletionHelperFile(configuration.getDatabases(), new File(dir, "databases")); franta-hg@80: writeBashCompletionHelperFile(configuration.getAllFormatters(), new File(dir, "formatters")); franta-hg@80: } catch (Exception e) { franta-hg@80: log.log(Level.WARNING, "Unable to generate Bash completion helper files", e); franta-hg@80: } franta-hg@80: } franta-hg@80: } franta-hg@80: franta-hg@80: private void writeBashCompletionHelperFile(Collection items, File target) throws FileNotFoundException { franta-hg@80: if (Constants.CONFIG_FILE.lastModified() > target.lastModified()) { franta-hg@80: try (PrintWriter fw = new PrintWriter(target)) { franta-hg@80: for (NameIdentified dd : items) { franta-hg@80: fw.println(dd.getName()); franta-hg@80: } franta-hg@80: fw.close(); franta-hg@80: log.log(Level.FINE, "Bash completion helper file was written: {0}", target); franta-hg@80: } franta-hg@80: } else { franta-hg@80: log.log(Level.FINER, "Not writing Bash completion helper file: {0} because configuration {1} has not been changed", new Object[]{target, Constants.CONFIG_FILE}); franta-hg@80: } franta-hg@80: } franta-hg@1: }