package cosmos.sql.console; import java.io.File; import java.io.IOException; import java.io.PrintWriter; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.List; import java.util.Properties; import jline.console.ConsoleReader; import jline.console.history.FileHistory; import jline.console.history.History; import jline.console.history.MemoryHistory; import org.apache.accumulo.core.client.Connector; import org.apache.accumulo.core.client.ZooKeeperInstance; import org.apache.accumulo.core.client.security.tokens.PasswordToken; import org.apache.accumulo.core.security.Authorizations; import org.apache.accumulo.minicluster.MiniAccumuloCluster; import org.apache.accumulo.minicluster.MiniAccumuloConfig; import org.apache.commons.io.FileUtils; import org.apache.commons.lang.StringUtils; import org.mediawiki.xml.export_0.MediaWikiType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.beust.jcommander.JCommander; import com.beust.jcommander.Parameter; import com.google.common.base.Stopwatch; import com.google.common.collect.Lists; import com.google.common.io.Files; import cosmos.Cosmos; import cosmos.impl.CosmosImpl; import cosmos.options.Defaults; import cosmos.records.Record; import cosmos.results.integration.CosmosIntegrationSetup; import cosmos.sql.CosmosDriver; import cosmos.sql.impl.CosmosSql; import cosmos.store.PersistedStores; import cosmos.store.Store; public class CosmosConsole { private static final Logger log = LoggerFactory.getLogger(CosmosConsole.class); private static final String EXIT = "exit"; private static final String PROMPT = "cosmos> "; public static class CosmosConsoleOptions { @Parameter(names = {"--use-mini", "-m"}, description = "Start an Accumulo MiniCluster") public boolean useMiniCluster = false; @Parameter(names = {"--use-real", "-r"}, description = "Connecto to a live Accumulo instance") public boolean useRealInstance = false; @Parameter(names = {"--loaddata", "-l"}, description = "Load some data into the Accumulo instance") public boolean loadData = false; @Parameter(names = {"--help", "-h"}, description = "Prints a help message", help = true) public boolean help = false; public CosmosConsoleOptions() {} } public static class AccumuloInstanceOptions { @Parameter(names = {"--zookeepers", "-z"}, description = "CSV of zookeepers to use") public String zookeepers; @Parameter(names = {"--instance", "-i"}, description = "Accumulo instance name") public String instanceName; @Parameter(names = {"--username", "-u"}, description = "Accumulo user name") public String username; @Parameter(names = {"--password", "-p"}, description = "Accumulo user password") public String password; @Parameter(names = {"--id", "-s"}, description = "The UUID of the SortableResult to use") public String uuid; } protected Cosmos cosmos; protected Connection connection; public CosmosConsole(Cosmos c, Connection connection) { this.cosmos = c; this.connection = connection; } /** * Load the History from disk if a $HOME directory is defined, otherwise fall back to using an in-memory history object * * @return * @throws IOException */ protected History getJLineHistory() throws IOException { String home = System.getProperty("HOME"); if (null == home) { home = System.getenv("HOME"); } // Get the home directory File homeDir = new File(home); // Make sure it actually exists if (homeDir.exists() && homeDir.isDirectory()) { // Check for, and create if necessary, the directory for cosmos to use File historyDir = new File(homeDir, ".cosmos"); if (!historyDir.exists() && !historyDir.mkdirs()) { log.warn("Could not create directory for history at {}, using temporary history.", historyDir); } // Get a file for jline history File historyFile = new File(historyDir, "history"); return new FileHistory(historyFile); } else { log.warn("Home directory not found: {}, using temporary history", homeDir); return new MemoryHistory(); } } public void startRepl() { ConsoleReader reader = null; History history = null; final TableOutputFormat tableOutput = new TableOutputFormat(); try { reader = new ConsoleReader(); PrintWriter sysout = new PrintWriter(reader.getOutput()); // Try to load the history and set it on the ConsoleReader history = getJLineHistory(); if (null != history) { reader.setHistory(history); } String line; while ((line = reader.readLine(PROMPT)) != null) { line = StringUtils.strip(line); // Eat a blank line if (StringUtils.isBlank(line)) { continue; } // Quit on `exit` if (EXIT.equals(line)) { break; } Statement statement = null; try { statement = connection.createStatement(); Stopwatch timer = new Stopwatch(); timer.start(); final ResultSet resultSet = statement.executeQuery(line); tableOutput.print(resultSet, timer); } catch (SQLException e) { log.error("SQLException", e); sysout.println(); } catch (RuntimeException e) { log.error("RuntimeException", e); sysout.println(); } finally { sysout.flush(); } } } catch (IOException e) { log.error("Caught IOException", e); } finally { if (null != reader) { reader.shutdown(); } if (null != history) { if (FileHistory.class.isAssignableFrom(history.getClass())) { try { ((FileHistory) history).flush(); } catch (IOException e) { log.warn("Couldn't flush history to disk", e); } } } } } public static void main(String[] args) throws Exception { final File tmp = Files.createTempDir(); final String passwd = "foobar"; MiniAccumuloCluster mac = null; Cosmos cosmos = null; CosmosSql cosmosSql = null; log.info("Starting Cosmos Console"); // Make sure optiq can load the class we need for sql try { Class.forName(CosmosDriver.class.getCanonicalName()); } catch (ClassNotFoundException e) { throw new RuntimeException("driver not found", e); } try { CosmosConsoleOptions consoleOptions = new CosmosConsoleOptions(); AccumuloInstanceOptions accumuloOptions = new AccumuloInstanceOptions(); new JCommander(new Object[] {consoleOptions, accumuloOptions}, args); Store id = null; Connector connector = null; Authorizations auths = new Authorizations("en"); if (consoleOptions.useMiniCluster) { log.info("Starting Accumulo MiniCluster"); MiniAccumuloConfig macConf = new MiniAccumuloConfig(tmp, passwd); macConf.setNumTservers(2); mac = new MiniAccumuloCluster(macConf); mac.start(); cosmos = new CosmosImpl(mac.getZooKeepers()); ZooKeeperInstance instance = new ZooKeeperInstance(mac.getInstanceName(), mac.getZooKeepers()); connector = instance.getConnector("root", new PasswordToken(passwd)); // Set this since we know we need it for the wiki test data connector.securityOperations().changeUserAuthorizations("root", auths); } else if (consoleOptions.useRealInstance) { if (null == accumuloOptions.instanceName || null == accumuloOptions.zookeepers || null == accumuloOptions.username || null == accumuloOptions.password) { log.error("If an ID for preloaded data is provided, connection information must also be provided"); System.exit(1); return; } ZooKeeperInstance instance = new ZooKeeperInstance(accumuloOptions.instanceName, accumuloOptions.zookeepers); connector = instance.getConnector(accumuloOptions.username, new PasswordToken(accumuloOptions.password)); cosmos = new CosmosImpl(accumuloOptions.zookeepers); } else { log.error("You must choose to use either an Accumulo minicluster or a real Accumulo instance"); System.exit(1); } if (consoleOptions.loadData) { log.info("Loading wiki data"); // Pre-load jaxb CosmosIntegrationSetup.initializeJaxb(); // Load all of the wikis.. they're not *that* big List<Record<?>> inputData = Lists.newArrayListWithExpectedSize(200000); for (MediaWikiType wiki : Lists.newArrayList(CosmosIntegrationSetup.getWiki1(), CosmosIntegrationSetup.getWiki2(), CosmosIntegrationSetup.getWiki3(), CosmosIntegrationSetup.getWiki4(), CosmosIntegrationSetup.getWiki5())) { inputData.addAll(CosmosIntegrationSetup.wikiToMultimap(wiki)); } id = new Store(connector, connector.securityOperations().getUserAuthorizations("root"), CosmosIntegrationSetup.ALL_INDEXES); // Override the default BatchWriterConfig (50M memory buffer) // BatchWriterConfig bwConfig = new BatchWriterConfig(); // bwConfig.setMaxMemory(1024 * 1024 * 100); // id.setWriterConfig(bwConfig); log.info("Sending data through Cosmos"); cosmos.register(id); cosmos.addResults(id, inputData); cosmos.finalize(id); log.info("Loaded wiki data with an id of {}", id.uuid()); id.sendTraces(); // Serialize this Store so we can reconstitute it again later PersistedStores.store(id); } cosmosSql = new CosmosSql(cosmos, connector, Defaults.METADATA_TABLE, auths); CosmosDriver driver = new CosmosDriver(cosmosSql, "cosmos", connector, auths, Defaults.METADATA_TABLE); Connection connection = DriverManager.getConnection(CosmosDriver.jdbcConnectionString(driver) + "//localhost", new Properties()); CosmosConsole console = new CosmosConsole(cosmos, connection); console.startRepl(); } finally { if (null != cosmos) { cosmos.close(); } try { if (null != mac) { mac.stop(); } } catch (Exception e) { log.error("Error stopping Accumulo minicluster", e); } FileUtils.deleteDirectory(tmp); } } }