/* * Copyright 2013 Eediom Inc. * * 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.araqne.logdb.impl; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; import org.araqne.api.ScriptContext; import org.araqne.logdb.AccountService; import org.araqne.logdb.QueryContext; import org.araqne.logdb.QueryParseException; import org.araqne.logdb.QueryService; import org.araqne.logdb.QueryResultSet; import org.araqne.logdb.Permission; import org.araqne.logdb.Query; import org.araqne.logdb.QueryStopReason; import org.araqne.logdb.RunMode; import org.araqne.logdb.SavedResult; import org.araqne.logdb.SavedResultManager; import org.araqne.logdb.Session; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class Console { private final Logger logger = LoggerFactory.getLogger(Console.class); private ScriptContext context; private QueryService queryService; private AccountService accountService; private SavedResultManager savedResultManager; private Session session; public Console(ScriptContext context, AccountService accountService, QueryService queryService, SavedResultManager savedResultManager) { this.context = context; this.accountService = accountService; this.queryService = queryService; this.savedResultManager = savedResultManager; } private String getPrompt(Session session) { return session.getLoginName() + "@logdb> "; } public void run(String loginName) { try { context.print("password? "); String password = context.readPassword(); session = accountService.login(loginName, password); context.println("Araqne LogDB Console"); context.println("Type \"help\" for more information"); while (true) { context.print(getPrompt(session)); String line = context.readLine().trim(); if (line.trim().equals("quit") || line.trim().equals("exit")) break; handle(line); } } catch (InterruptedException e) { context.println("interrupted"); } catch (Throwable t) { if (t.getMessage() != null) context.println(t.getMessage()); else context.println(t.toString()); } finally { if (session != null) { accountService.logout(session); context.println("logout"); } } } private void handle(String line) { if (line.trim().isEmpty()) return; try { String[] args = line.split(" "); String command = args[0].trim(); if (command.equals("help")) { help(); } else if (command.equals("create_account")) { if (args.length < 2) context.println("Usage: create_account [login name]"); else createAccount(args[1]); } else if (command.equals("remove_account")) { if (args.length < 2) context.println("Usage: remove_account [login name]"); else removeAccount(args[1]); } else if (command.equals("passwd")) { if (args.length < 1) context.println("Usage: passwd [login name]"); else changePassword(args.length >= 2 ? args[1] : null); } else if (command.equals("exec")) { if (args.length < 2) context.println("Usage: exec [file path]"); else fileQuery(args[1]); } else if (command.equals("queries")) { queries(args.length > 1 ? args[1] : null); } else if (command.equals("query")) { query(line.substring("query".length()).trim()); } else if (command.equals("create_query")) { createQuery(line.substring("create_query".length()).trim()); } else if (command.equals("start_query")) { if (args.length < 2) context.println("Usage: start_query [query id]"); else startQuery(Integer.valueOf(args[1])); } else if (command.equals("stop_query")) { if (args.length < 2) context.println("Usage: stop_query [query id]"); else stopQuery(Integer.valueOf(args[1])); } else if (command.equals("remove_query")) { if (args.length < 2) context.println("Usage: remove_query [query id]"); else removeQuery(Integer.valueOf(args[1])); } else if (command.equals("remove_all_queries")) { removeAllQueries(); } else if (command.equals("fetch")) { if (args.length < 4) context.println("Usage: fetch [query id] [offset] [limit]"); else { int id = Integer.valueOf(args[1]); long offset = Long.valueOf(args[2]); long limit = Long.valueOf(args[3]); fetch(id, offset, limit); } } else if (command.equals("grant_admin")) { if (args.length < 2) context.println("Usage: grant_admin [login name]"); else grantAdmin(args[1]); } else if (command.equals("revoke_admin")) { if (args.length < 2) context.println("Usage: revoke_admin [login name]"); else revokeAdmin(args[1]); } else if (command.equals("grant")) { if (args.length < 3) context.println("Usage: grant [login name] [table name]"); else grantPrivilege(args[1], args[2]); } else if (command.equals("revoke")) { if (args.length < 3) context.println("Usage: revoke [login name] [table name]"); else revokePrivilege(args[1], args[2]); } else if (command.equals("bg")) { if (args.length < 2) context.println("Usage: bg [query id]"); else setRunMode(true, args[1]); } else if (command.equals("fg")) { if (args.length < 2) context.println("Usage: fg [query id]"); else setRunMode(false, args[1]); } else if (command.equals("save")) { if (args.length < 3) context.println("Usage: save [query id] [title]"); else save(args[1], args[2]); } else { context.println("invalid syntax"); } } catch (QueryParseException t) { context.println(line); context.println(t.getMessage()); logger.error("araqne logdb: console fail", t); } catch (Throwable t) { context.println(t.getMessage()); logger.error("araqne logdb: console fail", t); } } private void help() { context.println("queries"); context.println("\tprint all queries initiated by this session"); context.println("query <query string>"); context.println("\tcreate, start and fetch query result at once"); context.println("create_query <query string>"); context.println("\tcreate query with specified query string, and return allocated query id"); context.println("start_query <query id>"); context.println("\tstart query"); context.println("stop_query <query_id>"); context.println("\tstop running query"); context.println("remove_query <query_id>"); context.println("\tstop and remove query"); context.println("fetch <query_id> <offset> <limit>"); context.println("\tfetch result set of specified window. you can fetch partial result before query is ended"); context.println("grant_admin <account>"); context.println("\tgrant admin role to specified account"); context.println("revoke_admin <account>"); context.println("\trevoke admin role from specified account"); context.println("grant <account> <table>"); context.println("\tgrant read table permission to specified account"); context.println("revoke <account> <table>"); context.println("\trevoke read table permission from specified account"); context.println("fg <query_id>"); context.println("\trun query in foreground"); context.println("bg <query_id>"); context.println("\trun query as a background task"); } private void createAccount(String loginName) throws InterruptedException { context.print("New password: "); String password = context.readPassword(); accountService.createAccount(session, loginName, password); context.println("created " + loginName); } private void removeAccount(String loginName) { accountService.removeAccount(session, loginName); context.println("removed " + loginName); } private void changePassword(String loginName) throws InterruptedException { if (loginName == null) loginName = session.getLoginName(); if (!loginName.equals(session.getLoginName()) && !session.isAdmin()) { context.println("no permission"); return; } if (accountService.getAccount(loginName) == null) { context.println("local account not found"); return; } context.println("Changing password for user " + loginName); if (!session.isAdmin()) { context.print("(current) password: "); String current = context.readPassword(); if (!accountService.verifyPassword(loginName, current)) { context.println("password mismatch"); return; } } context.print("New password: "); String password = context.readPassword(); context.print("Retype new password: "); String rePassword = context.readPassword(); if (!password.equals(rePassword)) { context.println("Sorry, passwords do not match"); return; } accountService.changePassword(session, loginName, rePassword); context.println("password changed"); } private void fileQuery(String filePath) throws IOException { File f = new File(filePath); if (!f.exists()) { context.println("query file not found: " + f.getAbsolutePath()); return; } if (!f.canRead()) { context.println("check query file permission: " + f.getAbsolutePath()); return; } BufferedReader br = null; FileInputStream is = null; StringBuilder sb = new StringBuilder(); try { is = new FileInputStream(f); br = new BufferedReader(new InputStreamReader(is, "utf-8")); while (true) { String line = br.readLine(); if (line == null) break; if (!line.trim().startsWith("#")) sb.append(" " + line); } } finally { if (is != null) { try { is.close(); } catch (IOException e) { } } } String query = sb.toString(); query(query); } private void queries(String queryFilter) { QueryPrintHelper.printQueries(context, queryService.getQueries(session), queryFilter); } private void query(String queryString) throws IOException { List<String> lines = new ArrayList<String>(); try { queryString = queryString.trim(); lines.add(queryString); if (queryString.endsWith("\\")) { while (true) { queryString = context.readLine().trim(); lines.add(queryString); if (!queryString.endsWith("\\")) break; } } } catch (InterruptedException e) { return; } int i = 0; StringBuilder sb = new StringBuilder(); for (String line : lines) { if (i++ != 0) sb.append(" "); if (line.endsWith("\\")) sb.append(line.substring(0, line.length() - 1)); else sb.append(line); } queryString = sb.toString(); long begin = System.nanoTime(); QueryContext ctx = new QueryContext(session); ctx.setSource("console"); Query lq = queryService.createQuery(ctx, queryString); queryService.startQuery(session, lq.getId()); do { try { Thread.sleep(50); } catch (InterruptedException e) { } } while (!lq.isFinished()); long duration = (System.nanoTime() - begin) / 1000000L; if (lq.getCause() != null) { context.println("query failure: " + lq.getCause().getMessage()); } else { long count = 0; QueryResultSet rs = null; try { rs = lq.getResultSet(); while (rs.hasNext()) { if (count == 1000) { context.printf("** result set size is over 1000. do you want to continue (y/N)? "); try { char read = context.read(); if (read != 'y' && read != 'Y') { throw new InterruptedException(); } } catch (InterruptedException e) { context.printf("\r\n"); break; } context.printf("\r"); } printMap(rs.next()); count++; } } finally { if (rs != null) rs.close(); } context.println(String.format("total %d rows, elapsed %.2fs", lq.getResultCount(), duration / (double) 1000)); } queryService.removeQuery(lq.getId()); } private void createQuery(String queryString) { QueryContext ctx = new QueryContext(session); ctx.setSource("console"); Query q = queryService.createQuery(ctx, queryString); context.println("created query " + q.getId()); } private void startQuery(int id) { Query q = queryService.getQuery(session, id); if (q == null) { context.println("query not found"); return; } queryService.startQuery(session, q.getId()); context.println("started query " + id); } @SuppressWarnings("unchecked") private void printMap(Map<String, Object> m) { boolean start = true; context.print("{"); List<String> keySet = new ArrayList<String>(m.keySet()); Collections.sort(keySet); for (String key : keySet) { if (start) start = false; else context.print(", "); context.print(key + "="); Object value = m.get(key); if (value instanceof Map) printMap((Map<String, Object>) value); else if (value == null) context.print("null"); else if (value.getClass().isArray()) { Class<?> c = value.getClass().getComponentType(); if (c == byte.class) context.print(encodeBinary((byte[]) value)); else context.print(Arrays.toString((Object[]) value)); } else context.print(value.toString()); } context.println("}"); } private String encodeBinary(byte[] b) { StringBuilder sb = new StringBuilder(b.length * 2); for (int i = 0; i < b.length; i++) { sb.append(String.format("%02x", b[i])); } return sb.toString(); } private void stopQuery(int id) { Query q = queryService.getQuery(session, id); if (q != null) { q.stop(QueryStopReason.UserRequest); context.println("stopped"); } else { context.println("query not found: " + id); } } private void removeQuery(int id) { queryService.removeQuery(session, id); context.println("removed query " + id); } private void fetch(int id, long offset, long limit) throws IOException { Query q = queryService.getQuery(session, id); if (q == null) { context.println("query not found"); return; } QueryResultSet result = q.getResultSet(); result.skip(offset); for (long i = 0; result.hasNext() && i < limit; i++) printMap(result.next()); } private void removeAllQueries() { for (Query q : queryService.getQueries(session)) { int id = q.getId(); queryService.removeQuery(session, id); context.println("removed query " + id); } context.println("cleared all queries"); } /** * @since 1.0.0 */ private void grantAdmin(String loginName) { accountService.grantAdmin(session, loginName); context.println("granted"); } /** * @since 1.0.0 */ private void revokeAdmin(String loginName) { accountService.revokeAdmin(session, loginName); context.println("revoked"); } private void grantPrivilege(String loginName, String tableName) { accountService.grantPrivilege(session, loginName, tableName, Permission.READ); context.println("granted"); } private void revokePrivilege(String loginName, String tableName) { accountService.revokePrivilege(session, loginName, tableName, Permission.READ); context.println("revoked"); } private void setRunMode(boolean background, String queryId) { int id = Integer.valueOf(queryId); Query q = queryService.getQuery(id); q.setRunMode(background ? RunMode.BACKGROUND : RunMode.FOREGROUND, new QueryContext(session)); context.println(background ? "run as a background task" : "run in the foreground"); } private void save(String queryId, String title) { int id = Integer.valueOf(queryId); Query q = queryService.getQuery(id); if (q == null) { context.println("query not found"); return; } QueryResultSet rs = null; try { rs = q.getResultSet(); long total = rs.getIndexPath().length() + rs.getDataPath().length(); SavedResult sr = new SavedResult(); sr.setStorageName(rs.getStorageName()); sr.setOwner(session.getLoginName()); sr.setQueryString(q.getQueryString()); sr.setTitle(title); sr.setIndexPath(rs.getIndexPath().getAbsolutePath()); sr.setDataPath(rs.getDataPath().getAbsolutePath()); sr.setRowCount(rs.size()); sr.setFileSize(total); savedResultManager.saveResult(sr); context.println("saved " + total + " bytes"); } catch (IOException e) { logger.error("araqne logdb: cannot save result", e); context.println("save failed: " + e.getMessage()); } finally { if (rs != null) rs.close(); } } }