001 /* 002 // $Id: //open/mondrian/src/main/mondrian/tui/CmdRunner.java#48 $ 003 // This software is subject to the terms of the Common Public License 004 // Agreement, available at the following URL: 005 // http://www.opensource.org/licenses/cpl.html. 006 // Copyright (C) 2005-2008 Julian Hyde and others 007 // All Rights Reserved. 008 // You must accept the terms of that agreement to use this software. 009 */ 010 011 package mondrian.tui; 012 013 import mondrian.olap.*; 014 import mondrian.olap.type.TypeUtil; 015 import mondrian.olap.fun.FunInfo; 016 import mondrian.rolap.RolapConnectionProperties; 017 import mondrian.rolap.RolapCube; 018 import org.apache.log4j.Level; 019 import org.apache.log4j.Logger; 020 import org.apache.log4j.LogManager; 021 import org.eigenbase.util.property.Property; 022 023 import java.io.*; 024 import java.text.NumberFormat; 025 import java.text.ParseException; 026 import java.util.ArrayList; 027 import java.util.Enumeration; 028 import java.util.Iterator; 029 import java.util.List; 030 import java.util.HashMap; 031 import java.util.Map; 032 import java.util.regex.Matcher; 033 import java.util.regex.Pattern; 034 import java.lang.reflect.Field; 035 import java.lang.reflect.Modifier; 036 037 /** 038 * Command line utility which reads and executes MDX commands. 039 * 040 * <p>TODO: describe how to use this class.</p> 041 * 042 * @author Richard Emberson 043 * @version $Id: //open/mondrian/src/main/mondrian/tui/CmdRunner.java#48 $ 044 */ 045 public class CmdRunner { 046 047 private static final String nl = Util.nl; 048 049 private static boolean RELOAD_CONNECTION = true; 050 private static String CATALOG_NAME = "FoodMart"; 051 052 private static final Map<Object, String> paraNameValues = 053 new HashMap<Object, String>(); 054 055 private static String[][] commentDelim; 056 private static char[] commentStartChars; 057 private static boolean allowNestedComments; 058 059 private final Options options; 060 private long queryTime; 061 private long totalQueryTime; 062 private String filename; 063 private String mdxCmd; 064 private String mdxResult; 065 private String error; 066 private String stack; 067 private String connectString; 068 private Connection connection; 069 private final PrintWriter out; 070 071 static { 072 setDefaultCommentState(); 073 } 074 075 /** 076 * Creates a <code>CmdRunner</code>. 077 * 078 * @param options Option set, or null to use default options 079 * @param out Output writer, or null to use {@link System#out}. 080 */ 081 public CmdRunner(Options options, PrintWriter out) { 082 if (options == null) { 083 options = new Options(); 084 } 085 this.options = options; 086 this.filename = null; 087 this.mdxResult = null; 088 this.error = null; 089 this.queryTime = -1; 090 if (out == null) { 091 out = new PrintWriter(System.out); 092 } 093 this.out = out; 094 } 095 096 public void setTimeQueries(boolean timeQueries) { 097 this.options.timeQueries = timeQueries; 098 } 099 100 public boolean getTimeQueries() { 101 return options.timeQueries; 102 } 103 104 public long getQueryTime() { 105 return queryTime; 106 } 107 108 public long getTotalQueryTime() { 109 return totalQueryTime; 110 } 111 112 public void noCubeCaching() { 113 Cube[] cubes = getCubes(); 114 for (Cube cube : cubes) { 115 RolapCube rcube = (RolapCube) cube; 116 rcube.setCacheAggregations(false); 117 } 118 } 119 120 void setError(String s) { 121 this.error = s; 122 } 123 124 void setError(Throwable t) { 125 this.error = formatError(t); 126 StringWriter sw = new StringWriter(); 127 PrintWriter pw = new PrintWriter(sw); 128 t.printStackTrace(pw); 129 pw.flush(); 130 this.stack = sw.toString(); 131 } 132 133 void clearError() { 134 this.error = null; 135 this.stack = null; 136 } 137 138 private String formatError(Throwable mex) { 139 String message = mex.getMessage(); 140 if (message == null) { 141 message = mex.toString(); 142 } 143 if (mex.getCause() != null && mex.getCause() != mex) { 144 message = message + nl + formatError(mex.getCause()); 145 } 146 return message; 147 } 148 149 public static void listPropertyNames(StringBuilder buf) { 150 PropertyInfo propertyInfo = 151 new PropertyInfo(MondrianProperties.instance()); 152 for (int i = 0; i < propertyInfo.size(); i++) { 153 buf.append(propertyInfo.getProperty(i).getPath()); 154 buf.append(nl); 155 } 156 } 157 158 public static void listPropertiesAll(StringBuilder buf) { 159 PropertyInfo propertyInfo = 160 new PropertyInfo(MondrianProperties.instance()); 161 for (int i = 0; i < propertyInfo.size(); i++) { 162 String propertyName = propertyInfo.getPropertyName(i); 163 String propertyValue = propertyInfo.getProperty(i).getString(); 164 buf.append(propertyName); 165 buf.append('='); 166 buf.append(propertyValue); 167 buf.append(nl); 168 } 169 } 170 171 /** 172 * Returns the value of a property, or null if it is not set. 173 */ 174 private static String getPropertyValue(String propertyName) { 175 final Property property = PropertyInfo.lookupProperty( 176 MondrianProperties.instance(), 177 propertyName); 178 return property.isSet() ? 179 property.getString() : 180 null; 181 } 182 183 public static void listProperty(String propertyName, StringBuilder buf) { 184 buf.append(getPropertyValue(propertyName)); 185 } 186 187 public static boolean isProperty(String propertyName) { 188 final Property property = PropertyInfo.lookupProperty( 189 MondrianProperties.instance(), 190 propertyName); 191 return property != null; 192 } 193 194 public static boolean setProperty(String name, String value) { 195 final Property property = PropertyInfo.lookupProperty( 196 MondrianProperties.instance(), 197 name); 198 String oldValue = property.getString(); 199 if (! Util.equals(oldValue, value)) { 200 property.setString(value); 201 return true; 202 } else { 203 return false; 204 } 205 } 206 207 public void loadParameters(Query query) { 208 Parameter[] params = query.getParameters(); 209 for (Parameter param : params) { 210 loadParameter(query, param); 211 } 212 } 213 214 /** 215 * Looks up the definition of a property with a given name. 216 */ 217 private static class PropertyInfo { 218 private final List<Property> propertyList = new ArrayList<Property>(); 219 private final List<String> propertyNameList = new ArrayList<String>(); 220 221 PropertyInfo(MondrianProperties properties) { 222 final Class<? extends Object> clazz = properties.getClass(); 223 final Field[] fields = clazz.getFields(); 224 for (Field field : fields) { 225 if (!Modifier.isPublic(field.getModifiers()) || 226 Modifier.isStatic(field.getModifiers()) || 227 !Property.class.isAssignableFrom( 228 field.getType())) { 229 continue; 230 } 231 final Property property; 232 try { 233 property = (Property) field.get(properties); 234 } catch (IllegalAccessException e) { 235 continue; 236 } 237 propertyList.add(property); 238 propertyNameList.add(field.getName()); 239 } 240 } 241 242 public int size() { 243 return propertyList.size(); 244 } 245 246 public Property getProperty(int i) { 247 return propertyList.get(i); 248 } 249 250 public String getPropertyName(int i) { 251 return propertyNameList.get(i); 252 } 253 254 /** 255 * Looks up the definition of a property with a given name. 256 */ 257 public static Property lookupProperty( 258 MondrianProperties properties, 259 String propertyName) 260 { 261 final Class<? extends Object> clazz = properties.getClass(); 262 final Field field; 263 try { 264 field = clazz.getField(propertyName); 265 } catch (NoSuchFieldException e) { 266 return null; 267 } 268 if (!Modifier.isPublic(field.getModifiers()) || 269 Modifier.isStatic(field.getModifiers()) || 270 !Property.class.isAssignableFrom(field.getType())) { 271 return null; 272 } 273 try { 274 return (Property) field.get(properties); 275 } catch (IllegalAccessException e) { 276 return null; 277 } 278 } 279 } 280 281 private static class Expr { 282 enum Type { 283 STRING, 284 NUMERIC, 285 MEMBER 286 } 287 288 final Object value; 289 final Type type; 290 Expr(Object value, Type type) { 291 this.value = value; 292 this.type = type; 293 } 294 } 295 296 public void loadParameter(Query query, Parameter param) { 297 int category = TypeUtil.typeToCategory(param.getType()); 298 String name = param.getName(); 299 String value = CmdRunner.paraNameValues.get(name); 300 debug("loadParameter: name=" + name + ", value=" + value); 301 if (value == null) { 302 return; 303 } 304 Expr expr = parseParameter(value); 305 if (expr == null) { 306 return; 307 } 308 Expr.Type type = expr.type; 309 // found the parameter with the given name in the query 310 switch (category) { 311 case Category.Numeric: 312 if (type != Expr.Type.NUMERIC) { 313 String msg = "For parameter named \"" 314 + name 315 + "\" of Catetory.Numeric, " 316 + "the value was type \"" 317 + type 318 + "\""; 319 throw new IllegalArgumentException(msg); 320 } 321 break; 322 case Category.String: 323 if (type != Expr.Type.STRING) { 324 String msg = "For parameter named \"" 325 + name 326 + "\" of Catetory.String, " 327 + "the value was type \"" 328 + type 329 + "\""; 330 throw new IllegalArgumentException(msg); 331 } 332 break; 333 334 case Category.Member: 335 if (type != Expr.Type.MEMBER) { 336 String msg = "For parameter named \"" 337 + name 338 + "\" of Catetory.Member, " 339 + "the value was type \"" 340 + type 341 + "\""; 342 throw new IllegalArgumentException(msg); 343 } 344 break; 345 346 default: 347 throw Util.newInternal("unexpected category " + category); 348 } 349 query.setParameter(param.getName(), String.valueOf(expr.value)); 350 } 351 352 static NumberFormat nf = NumberFormat.getInstance(); 353 354 // this is taken from JPivot 355 public Expr parseParameter(String value) { 356 // is it a String (enclose in double or single quotes ? 357 String trimmed = value.trim(); 358 int len = trimmed.length(); 359 if (trimmed.charAt(0) == '"' && trimmed.charAt(len - 1) == '"') { 360 debug("parseParameter. STRING_TYPE: " + trimmed); 361 return new Expr( 362 trimmed.substring(1, trimmed.length() - 1), 363 Expr.Type.STRING); 364 } 365 if (trimmed.charAt(0) == '\'' && trimmed.charAt(len - 1) == '\'') { 366 debug("parseParameter. STRING_TYPE: " + trimmed); 367 return new Expr( 368 trimmed.substring(1, trimmed.length() - 1), 369 Expr.Type.STRING); 370 } 371 372 // is it a Number ? 373 Number number = null; 374 try { 375 number = nf.parse(trimmed); 376 } catch (ParseException pex) { 377 // nothing to do, should be member 378 } 379 if (number != null) { 380 debug("parseParameter. NUMERIC_TYPE: " + number); 381 return new Expr(number, Expr.Type.NUMERIC); 382 } 383 384 debug("parseParameter. MEMBER_TYPE: " + trimmed); 385 Query query = this.connection.parseQuery(this.mdxCmd); 386 // dont have to execute 387 //this.connection.execute(query); 388 389 // assume member, dimension, hierarchy, level 390 OlapElement element = Util.lookup(query, Util.parseIdentifier(trimmed)); 391 392 debug("parseParameter. exp=" 393 +((element == null) ? "null" : element.getClass().getName())); 394 395 if (element instanceof Member) { 396 Member member = (Member) element; 397 return new Expr(member, Expr.Type.MEMBER); 398 } else if (element instanceof mondrian.olap.Level) { 399 mondrian.olap.Level level = (mondrian.olap.Level) element; 400 return new Expr(level, Expr.Type.MEMBER); 401 } else if (element instanceof Hierarchy) { 402 Hierarchy hier = (Hierarchy) element; 403 return new Expr(hier, Expr.Type.MEMBER); 404 } else if (element instanceof Dimension) { 405 Dimension dim = (Dimension) element; 406 return new Expr(dim, Expr.Type.MEMBER); 407 } 408 return null; 409 } 410 411 public static void listParameterNameValues(StringBuilder buf) { 412 for (Map.Entry<Object, String> e : CmdRunner.paraNameValues.entrySet()) { 413 buf.append(e.getKey()); 414 buf.append('='); 415 buf.append(e.getValue()); 416 buf.append(nl); 417 } 418 } 419 420 public static void listParam(String name, StringBuilder buf) { 421 String v = CmdRunner.paraNameValues.get(name); 422 buf.append(v); 423 } 424 425 public static boolean isParam(String name) { 426 String v = CmdRunner.paraNameValues.get(name); 427 return (v != null); 428 } 429 430 public static void setParameter(String name, String value) { 431 if (name == null) { 432 CmdRunner.paraNameValues.clear(); 433 } else { 434 if (value == null) { 435 CmdRunner.paraNameValues.remove(name); 436 } else { 437 CmdRunner.paraNameValues.put(name, value); 438 } 439 } 440 } 441 442 ///////////////////////////////////////////////////////////////////////// 443 // 444 // cubes 445 // 446 public Cube[] getCubes() { 447 Connection conn = getConnection(); 448 return conn.getSchemaReader().getCubes(); 449 } 450 451 public Cube getCube(String name) { 452 Cube[] cubes = getCubes(); 453 for (Cube cube : cubes) { 454 if (cube.getName().equals(name)) { 455 return cube; 456 } 457 } 458 return null; 459 } 460 461 public void listCubeName(StringBuilder buf) { 462 Cube[] cubes = getCubes(); 463 for (Cube cube : cubes) { 464 buf.append(cube.getName()); 465 buf.append(nl); 466 } 467 } 468 469 public void listCubeAttribues(String name, StringBuilder buf) { 470 Cube cube = getCube(name); 471 if (cube == null) { 472 buf.append("No cube found with name \""); 473 buf.append(name); 474 buf.append("\""); 475 } else { 476 RolapCube rcube = (RolapCube) cube; 477 buf.append("facttable="); 478 buf.append(rcube.getStar().getFactTable().getAlias()); 479 buf.append(nl); 480 buf.append("caching="); 481 buf.append(rcube.isCacheAggregations()); 482 buf.append(nl); 483 } 484 } 485 486 public void executeCubeCommand( 487 String cubename, 488 String command, 489 StringBuilder buf) { 490 Cube cube = getCube(cubename); 491 if (cube == null) { 492 buf.append("No cube found with name \""); 493 buf.append(cubename); 494 buf.append("\""); 495 } else { 496 if (command.equals("clearCache")) { 497 RolapCube rcube = (RolapCube) cube; 498 rcube.clearCachedAggregations(); 499 } else { 500 buf.append("For cube \""); 501 buf.append(cubename); 502 buf.append("\" there is no command \""); 503 buf.append(command); 504 buf.append("\""); 505 } 506 } 507 } 508 509 public void setCubeAttribute( 510 String cubename, 511 String name, 512 String value, 513 StringBuilder buf) { 514 Cube cube = getCube(cubename); 515 if (cube == null) { 516 buf.append("No cube found with name \""); 517 buf.append(cubename); 518 buf.append("\""); 519 } else { 520 if (name.equals("caching")) { 521 RolapCube rcube = (RolapCube) cube; 522 boolean isCache = Boolean.valueOf(value); 523 rcube.setCacheAggregations(isCache); 524 } else { 525 buf.append("For cube \""); 526 buf.append(cubename); 527 buf.append("\" there is no attribute \""); 528 buf.append(name); 529 buf.append("\""); 530 } 531 } 532 } 533 // 534 ///////////////////////////////////////////////////////////////////////// 535 536 /** 537 * Executes a query and returns the result as a string. 538 * 539 * @param queryString MDX query text 540 * @return result String 541 */ 542 public String execute(String queryString) { 543 Result result = runQuery(queryString, true); 544 if (this.options.highCardResults) { 545 return highCardToString(result); 546 } else { 547 return toString(result); 548 } 549 } 550 551 /** 552 * Executes a query and returns the result. 553 * 554 * @param queryString MDX query text 555 * @return a {@link Result} object 556 */ 557 public Result runQuery(String queryString, boolean loadParams) { 558 debug("CmdRunner.runQuery: TOP"); 559 Result result = null; 560 long start = System.currentTimeMillis(); 561 try { 562 this.connection = getConnection(); 563 debug("CmdRunner.runQuery: AFTER getConnection"); 564 Query query = this.connection.parseQuery(queryString); 565 debug("CmdRunner.runQuery: AFTER parseQuery"); 566 if (loadParams) { 567 loadParameters(query); 568 } 569 start = System.currentTimeMillis(); 570 result = this.connection.execute(query); 571 } finally { 572 queryTime = (System.currentTimeMillis() - start); 573 totalQueryTime += queryTime; 574 debug("CmdRunner.runQuery: BOTTOM"); 575 } 576 return result; 577 } 578 579 580 /** 581 * Converts a {@link Result} object to a string 582 * 583 * @return String version of mondrian Result object. 584 */ 585 public String toString(Result result) { 586 StringWriter sw = new StringWriter(); 587 PrintWriter pw = new PrintWriter(sw); 588 result.print(pw); 589 pw.flush(); 590 return sw.toString(); 591 } 592 /** 593 * Converts a {@link Result} object to a string printing to standard 594 * output directly, without buffering. 595 * 596 * @return null String since output is dump directly to stdout. 597 */ 598 public String highCardToString(Result result) { 599 result.print(new PrintWriter(System.out, true)); 600 return null; 601 } 602 603 604 public void makeConnectString() { 605 String connectString = CmdRunner.getConnectStringProperty(); 606 debug("CmdRunner.makeConnectString: connectString=" + connectString); 607 608 Util.PropertyList connectProperties; 609 if (connectString == null || connectString.equals("")) { 610 // create new and add provider 611 connectProperties = new Util.PropertyList(); 612 connectProperties.put(RolapConnectionProperties.Provider.name(),"mondrian"); 613 } else { 614 // load with existing connect string 615 connectProperties = Util.parseConnectString(connectString); 616 } 617 618 // override jdbc url 619 String jdbcURL = CmdRunner.getJdbcURLProperty(); 620 621 debug("CmdRunner.makeConnectString: jdbcURL=" + jdbcURL); 622 623 if (jdbcURL != null) { 624 // add jdbc url to connect string 625 connectProperties.put(RolapConnectionProperties.Jdbc.name(), jdbcURL); 626 } 627 628 // override jdbc drivers 629 String jdbcDrivers = CmdRunner.getJdbcDriversProperty(); 630 631 debug("CmdRunner.makeConnectString: jdbcDrivers=" + jdbcDrivers); 632 if (jdbcDrivers != null) { 633 // add jdbc drivers to connect string 634 connectProperties.put(RolapConnectionProperties.JdbcDrivers.name(), jdbcDrivers); 635 } 636 637 // override catalog url 638 String catalogURL = CmdRunner.getCatalogURLProperty(); 639 640 debug("CmdRunner.makeConnectString: catalogURL=" + catalogURL); 641 642 if (catalogURL != null) { 643 // add catalog url to connect string 644 connectProperties.put(RolapConnectionProperties.Catalog.name(), catalogURL); 645 } 646 647 // override JDBC user 648 String jdbcUser = CmdRunner.getJdbcUserProperty(); 649 650 debug("CmdRunner.makeConnectString: jdbcUser=" + jdbcUser); 651 652 if (jdbcUser != null) { 653 // add user to connect string 654 connectProperties.put(RolapConnectionProperties.JdbcUser.name(), jdbcUser); 655 } 656 657 // override JDBC password 658 String jdbcPassword = CmdRunner.getJdbcPasswordProperty(); 659 660 debug("CmdRunner.makeConnectString: jdbcPassword=" + jdbcPassword); 661 662 if (jdbcPassword != null) { 663 // add password to connect string 664 connectProperties.put(RolapConnectionProperties.JdbcPassword.name(), jdbcPassword); 665 } 666 667 if (options.roleName != null) { 668 connectProperties.put(RolapConnectionProperties.Role.name(), options.roleName); 669 } 670 671 debug("CmdRunner.makeConnectString: connectProperties=" + connectProperties); 672 673 this.connectString = connectProperties.toString(); 674 } 675 676 /** 677 * Gets a connection to Mondrian. 678 * 679 * @return Mondrian {@link Connection} 680 */ 681 public Connection getConnection() { 682 return getConnection(CmdRunner.RELOAD_CONNECTION); 683 } 684 685 /** 686 * Gets a Mondrian connection, creating a new one if fresh is true. 687 * 688 * @return mondrian Connection. 689 */ 690 public synchronized Connection getConnection(boolean fresh) { 691 // FIXME: fresh is currently ignored. 692 if (this.connectString == null) { 693 makeConnectString(); 694 } 695 if (this.connection == null) { 696 this.connection = 697 DriverManager.getConnection(this.connectString, null); 698 } 699 return this.connection; 700 } 701 public String getConnectString() { 702 return getConnectString(CmdRunner.RELOAD_CONNECTION); 703 } 704 public synchronized String getConnectString(boolean fresh) { 705 if (this.connectString == null) { 706 makeConnectString(); 707 } 708 return this.connectString; 709 } 710 711 ///////////////////////////////////////////////////////////////////////// 712 ///////////////////////////////////////////////////////////////////////// 713 // 714 // static methods 715 // 716 ///////////////////////////////////////////////////////////////////////// 717 ///////////////////////////////////////////////////////////////////////// 718 719 protected void debug(String msg) { 720 if (options.debug) { 721 out.println(msg); 722 } 723 } 724 725 ///////////////////////////////////////////////////////////////////////// 726 // properties 727 ///////////////////////////////////////////////////////////////////////// 728 protected static String getConnectStringProperty() { 729 return MondrianProperties.instance().TestConnectString.get(); 730 } 731 protected static String getJdbcURLProperty() { 732 return MondrianProperties.instance().TestJdbcURL.get(); 733 } 734 735 protected static String getJdbcUserProperty() { 736 return MondrianProperties.instance().TestJdbcUser.get(); 737 } 738 739 protected static String getJdbcPasswordProperty() { 740 return MondrianProperties.instance().TestJdbcPassword.get(); 741 } 742 protected static String getCatalogURLProperty() { 743 return MondrianProperties.instance().CatalogURL.get(); 744 } 745 protected static String getJdbcDriversProperty() { 746 return MondrianProperties.instance().JdbcDrivers.get(); 747 } 748 749 ///////////////////////////////////////////////////////////////////////// 750 // command loop 751 ///////////////////////////////////////////////////////////////////////// 752 753 protected void commandLoop(boolean interactive) throws IOException { 754 commandLoop( 755 new BufferedReader( 756 new InputStreamReader(System.in)), 757 interactive); 758 } 759 760 protected void commandLoop(File file) throws IOException { 761 // If we open a stream, then we close it. 762 FileReader in = new FileReader(file); 763 try { 764 commandLoop(new BufferedReader(in), false); 765 } finally { 766 try { 767 in.close(); 768 } catch (Exception ex) { 769 // ignore 770 } 771 } 772 } 773 774 protected void commandLoop(String mdxCmd, boolean interactive) 775 throws IOException { 776 777 StringReader is = new StringReader(mdxCmd); 778 commandLoop(is, interactive); 779 } 780 781 private static final String COMMAND_PROMPT_START = "> "; 782 private static final String COMMAND_PROMPT_MID = "? "; 783 784 /** 785 * The Command Loop where lines are read from the InputStream and 786 * interpreted. If interactive then prompts are printed. 787 * 788 * @param in Input reader (preferably buffered) 789 * @param interactive Whether the session is interactive 790 */ 791 protected void commandLoop(Reader in, boolean interactive) { 792 793 StringBuilder buf = new StringBuilder(2048); 794 boolean inMdxCmd = false; 795 String resultString = null; 796 797 for (;;) { 798 if (resultString != null) { 799 printResults(resultString); 800 printQueryTime(); 801 resultString = null; 802 buf.setLength(0); 803 } else if (interactive && (error != null)) { 804 printResults(error); 805 printQueryTime(); 806 } 807 if (interactive) { 808 if (inMdxCmd) { 809 out.print(COMMAND_PROMPT_MID); 810 } else { 811 out.print(COMMAND_PROMPT_START); 812 } 813 out.flush(); 814 } 815 if (!inMdxCmd) { 816 buf.setLength(0); 817 } 818 String line; 819 try { 820 line = readLine(in, inMdxCmd); 821 } catch (IOException e) { 822 throw new RuntimeException( 823 "Exception while reading command line", e); 824 } 825 if (line != null) { 826 line = line.trim(); 827 } 828 debug("line=" + line); 829 830 if (! inMdxCmd) { 831 // If not in the middle of reading an mdx query and 832 // we reach end of file on the stream, then we are over. 833 if (line == null) { 834 return; 835 } 836 } 837 838 // If not reading an mdx query, then check if the line is a 839 // user command. 840 if (! inMdxCmd) { 841 String cmd = line; 842 if (cmd.startsWith("help")) { 843 resultString = executeHelp(cmd); 844 } else if (cmd.startsWith("set")) { 845 resultString = executeSet(cmd); 846 } else if (cmd.startsWith("log")) { 847 resultString = executeLog(cmd); 848 } else if (cmd.startsWith("file")) { 849 resultString = executeFile(cmd); 850 } else if (cmd.startsWith("list")) { 851 resultString = executeList(cmd); 852 } else if (cmd.startsWith("func")) { 853 resultString = executeFunc(cmd); 854 } else if (cmd.startsWith("param")) { 855 resultString = executeParam(cmd); 856 } else if (cmd.startsWith("cube")) { 857 resultString = executeCube(cmd); 858 } else if (cmd.startsWith("error")) { 859 resultString = executeError(cmd); 860 } else if (cmd.startsWith("echo")) { 861 resultString = executeEcho(cmd); 862 } else if (cmd.startsWith("expr")) { 863 resultString = executeExpr(cmd); 864 } else if (cmd.equals("=")) { 865 resultString = reExecuteMdxCmd(); 866 } else if (cmd.startsWith("exit")) { 867 break; 868 } 869 if (resultString != null) { 870 inMdxCmd = false; 871 continue; 872 } 873 } 874 875 // Are we ready to execute an mdx query. 876 if ((line == null) || 877 ((line.length() == 1) && 878 ((line.charAt(0) == EXECUTE_CHAR) || 879 (line.charAt(0) == CANCEL_CHAR)))) { 880 881 // If EXECUTE_CHAR, then execute, otherwise its the 882 // CANCEL_CHAR and simply empty buffer. 883 if ((line == null) || (line.charAt(0) == EXECUTE_CHAR)) { 884 String mdxCmd = buf.toString().trim(); 885 debug("mdxCmd=\"" + mdxCmd + "\""); 886 resultString = executeMdxCmd(mdxCmd); 887 } 888 889 inMdxCmd = false; 890 891 } else if (line.length() > 0) { 892 893 // OK, just add the line to the mdx query we are building. 894 inMdxCmd = true; 895 896 if (line.endsWith(SEMI_COLON_STRING)) { 897 // Remove the ';' character. 898 buf.append(line.substring(0, line.length() - 1)); 899 String mdxCmd = buf.toString().trim(); 900 debug("mdxCmd=\"" + mdxCmd + "\""); 901 resultString = executeMdxCmd(mdxCmd); 902 inMdxCmd = false; 903 } else { 904 buf.append(line); 905 // add carriage return so that query keeps formatting 906 buf.append(nl); 907 } 908 } 909 } 910 } 911 912 protected void printResults(String resultString) { 913 if (resultString != null) { 914 resultString = resultString.trim(); 915 if (resultString.length() > 0) { 916 out.println(resultString); 917 out.flush(); 918 } 919 } 920 } 921 protected void printQueryTime() { 922 if (options.timeQueries && (queryTime != -1)) { 923 out.println("time[" + queryTime + "ms]"); 924 out.flush(); 925 queryTime = -1; 926 } 927 } 928 929 /** 930 * Gather up a line ending in '\n' or EOF. 931 * Returns null if at EOF. 932 * Strip out comments. If a comment character appears within a 933 * string then its not a comment. Strings are defined with "\"" or 934 * "'" characters. Also, a string can span more than one line (a 935 * nice little complication). So, if we read a string, then we consume 936 * the whole string as part of the "line" returned, 937 * including EOL characters. 938 * If an escape character is seen '\\', then it and the next character 939 * is added to the line regardless of what the next character is. 940 */ 941 protected static String readLine(Reader reader, boolean inMdxCmd) 942 throws IOException { 943 944 StringBuilder buf = new StringBuilder(128); 945 StringBuilder line = new StringBuilder(128); 946 int offset; 947 int i = getLine(reader, line); 948 boolean inName = false; 949 950 for (offset = 0; offset < line.length(); offset++) { 951 char c = line.charAt(offset); 952 953 if (c == ESCAPE_CHAR) { 954 buf.append(ESCAPE_CHAR); 955 buf.append(line.charAt(++offset)); 956 } else if (!inName && 957 ((c == STRING_CHAR_1) || (c == STRING_CHAR_2))) { 958 i = readString(reader, line, offset, buf, i); 959 offset = 0; 960 } else { 961 int commentType=-1; 962 963 if (c == BRACKET_START) { 964 inName = true; 965 } else if (c == BRACKET_END) { 966 inName = false; 967 } else if (! inName) { 968 // check if we have the start of a comment block 969 // check if we have the start of a comment block 970 for (int x = 0; x < commentDelim.length; x++) { 971 if (c != commentStartChars[x]) { 972 continue; 973 } 974 String startComment = commentDelim[x][0]; 975 boolean foundCommentStart = true; 976 for (int j = 1; 977 j + offset < line.length() && j < startComment.length(); 978 j++) { 979 if (line.charAt(j + offset) != startComment.charAt(j)) { 980 foundCommentStart = false; 981 } 982 } 983 984 if (foundCommentStart) { 985 if (x == 0) { 986 // A '#' must be the first character on a line 987 if (offset == 0) { 988 commentType = x; 989 break; 990 } 991 } else { 992 commentType = x; 993 break; 994 } 995 } 996 } 997 } 998 999 // -1 means no comment 1000 if (commentType == -1) { 1001 buf.append(c); 1002 } else { 1003 // check for comment to end of line comment 1004 if (commentDelim[commentType][1] == null) { 1005 break; 1006 } else { 1007 // handle delimited comment block 1008 i = readBlock(reader, line, offset, 1009 commentDelim[commentType][0], 1010 commentDelim[commentType][1], 1011 false, false, buf, i); 1012 offset = 0; 1013 } 1014 } 1015 } 1016 } 1017 1018 if (i == -1 && buf.length() == 0) { 1019 return null; 1020 } else { 1021 return buf.toString(); 1022 } 1023 } 1024 1025 /** 1026 * Read the next line of input. Return the terminating character, 1027 * -1 for end of file, or \n or \r. Add \n and \r to the end of the 1028 * buffer to be included in strings and comment blocks. 1029 */ 1030 protected static int getLine(Reader reader, StringBuilder line) 1031 throws IOException { 1032 1033 line.setLength(0); 1034 for (;;) { 1035 int i = reader.read(); 1036 1037 if (i == -1) { 1038 return i; 1039 } 1040 1041 line.append((char)i); 1042 1043 if (i == '\n' || i == '\r') { 1044 return i; 1045 } 1046 } 1047 } 1048 1049 /** 1050 * Start of a string, read all of it even if it spans 1051 * more than one line adding each line's <cr> to the 1052 * buffer. 1053 */ 1054 protected static int readString( 1055 Reader reader, 1056 StringBuilder line, 1057 int offset, 1058 StringBuilder buf, 1059 int i) 1060 throws IOException { 1061 1062 String delim = line.substring(offset, offset + 1); 1063 return readBlock(reader, line, offset, delim, delim, true, true, buf, i); 1064 } 1065 1066 /** 1067 * Start of a delimted block, read all of it even if it spans 1068 * more than one line adding each line's <cr> to the 1069 * buffer. 1070 * 1071 * A delimited block is a delimited comment (/\* ... *\/), or a string. 1072 */ 1073 protected static int readBlock( 1074 Reader reader, 1075 StringBuilder line, 1076 int offset, 1077 final String startDelim, 1078 final String endDelim, 1079 final boolean allowEscape, 1080 final boolean addToBuf, 1081 StringBuilder buf, 1082 int i) 1083 throws IOException { 1084 1085 int depth = 1; 1086 if (addToBuf) { 1087 buf.append(startDelim); 1088 } 1089 offset += startDelim.length(); 1090 1091 for (;;) { 1092 // see if we are at the end of the block 1093 if (line.substring(offset).startsWith(endDelim)) { 1094 if (addToBuf) { 1095 buf.append(endDelim); 1096 } 1097 offset += endDelim.length(); 1098 if (--depth == 0) { 1099 break; 1100 } 1101 // check for nested block 1102 } else if (allowNestedComments && 1103 line.substring(offset).startsWith(startDelim)) { 1104 if (addToBuf) { 1105 buf.append(startDelim); 1106 } 1107 offset += startDelim.length(); 1108 depth++; 1109 } else if (offset < line.length()) { 1110 // not at the end of line, so eat the next char 1111 char c = line.charAt(offset++); 1112 if (allowEscape && c == ESCAPE_CHAR) { 1113 if (addToBuf) { 1114 buf.append(ESCAPE_CHAR); 1115 } 1116 1117 if (offset < line.length()) { 1118 if (addToBuf) { 1119 buf.append(line.charAt(offset)); 1120 } 1121 offset++; 1122 } 1123 } else if (addToBuf) { 1124 buf.append(c); 1125 } 1126 } else { 1127 // finished a line; read in the next one and continue 1128 if (i == -1) { 1129 break; 1130 } 1131 i = getLine(reader, line); 1132 1133 // line will always contain EOL marker at least, unless at EOF 1134 offset = 0; 1135 if (line.length() == 0) { 1136 break; 1137 } 1138 } 1139 } 1140 1141 // remove to the end of the string, so caller starts at offset 0 1142 if (offset > 0) { 1143 line.delete(0, offset - 1); 1144 } 1145 1146 return i; 1147 } 1148 1149 ///////////////////////////////////////////////////////////////////////// 1150 // xmla file 1151 ///////////////////////////////////////////////////////////////////////// 1152 1153 /** 1154 * This is called to process a file containing XMLA as the contents 1155 * of SOAP xml. 1156 * 1157 */ 1158 protected void processSoapXmla(File file, int validateXmlaResponse) 1159 throws Exception { 1160 1161 1162 String catalogURL = CmdRunner.getCatalogURLProperty(); 1163 Map<String, String> catalogNameUrls = new HashMap<String, String>(); 1164 catalogNameUrls.put(CATALOG_NAME, catalogURL); 1165 1166 long start = System.currentTimeMillis(); 1167 1168 byte[] bytes = null; 1169 try { 1170 bytes = XmlaSupport.processSoapXmla(file, 1171 getConnectString(), 1172 catalogNameUrls, 1173 null); 1174 1175 } finally { 1176 queryTime = (System.currentTimeMillis() - start); 1177 totalQueryTime += queryTime; 1178 } 1179 1180 String response = new String(bytes); 1181 out.println(response); 1182 1183 switch (validateXmlaResponse) { 1184 case VALIDATE_NONE: 1185 break; 1186 case VALIDATE_TRANSFORM: 1187 XmlaSupport.validateSchemaSoapXmla(bytes); 1188 out.println("XML Data is Valid"); 1189 break; 1190 case VALIDATE_XPATH: 1191 XmlaSupport.validateSoapXmlaUsingXpath(bytes); 1192 out.println("XML Data is Valid"); 1193 break; 1194 } 1195 } 1196 1197 /** 1198 * This is called to process a file containing XMLA xml. 1199 * 1200 */ 1201 protected void processXmla(File file, int validateXmlaResponce) 1202 throws Exception { 1203 1204 1205 String catalogURL = CmdRunner.getCatalogURLProperty(); 1206 Map<String, String> catalogNameUrls = new HashMap<String, String>(); 1207 catalogNameUrls.put(CATALOG_NAME, catalogURL); 1208 1209 long start = System.currentTimeMillis(); 1210 1211 byte[] bytes = null; 1212 try { 1213 bytes = XmlaSupport.processXmla( 1214 file, 1215 getConnectString(), 1216 catalogNameUrls); 1217 1218 } finally { 1219 queryTime = (System.currentTimeMillis() - start); 1220 totalQueryTime += queryTime; 1221 } 1222 1223 String response = new String(bytes); 1224 out.println(response); 1225 1226 switch (validateXmlaResponce) { 1227 case VALIDATE_NONE: 1228 break; 1229 case VALIDATE_TRANSFORM: 1230 XmlaSupport.validateSchemaXmla(bytes); 1231 out.println("XML Data is Valid"); 1232 break; 1233 case VALIDATE_XPATH: 1234 XmlaSupport.validateXmlaUsingXpath(bytes); 1235 out.println("XML Data is Valid"); 1236 break; 1237 } 1238 } 1239 1240 ///////////////////////////////////////////////////////////////////////// 1241 // user commands and help messages 1242 ///////////////////////////////////////////////////////////////////////// 1243 private static final String INDENT = " "; 1244 1245 private static final int UNKNOWN_CMD = 0x0000; 1246 private static final int HELP_CMD = 0x0001; 1247 private static final int SET_CMD = 0x0002; 1248 private static final int LOG_CMD = 0x0004; 1249 private static final int FILE_CMD = 0x0008; 1250 private static final int LIST_CMD = 0x0010; 1251 private static final int MDX_CMD = 0x0020; 1252 private static final int FUNC_CMD = 0x0040; 1253 private static final int PARAM_CMD = 0x0080; 1254 private static final int CUBE_CMD = 0x0100; 1255 private static final int ERROR_CMD = 0x0200; 1256 private static final int ECHO_CMD = 0x0400; 1257 private static final int EXPR_CMD = 0x0800; 1258 private static final int EXIT_CMD = 0x1000; 1259 1260 private static final int ALL_CMD = HELP_CMD | 1261 SET_CMD | 1262 LOG_CMD | 1263 FILE_CMD | 1264 LIST_CMD | 1265 MDX_CMD | 1266 FUNC_CMD | 1267 PARAM_CMD | 1268 CUBE_CMD | 1269 ERROR_CMD | 1270 ECHO_CMD | 1271 EXPR_CMD | 1272 EXIT_CMD; 1273 1274 private static final char ESCAPE_CHAR = '\\'; 1275 private static final char EXECUTE_CHAR = '='; 1276 private static final char CANCEL_CHAR = '~'; 1277 private static final char STRING_CHAR_1 = '"'; 1278 private static final char STRING_CHAR_2 = '\''; 1279 private static final char BRACKET_START = '['; 1280 private static final char BRACKET_END = ']'; 1281 1282 private static final String SEMI_COLON_STRING = ";"; 1283 1284 ////////////////////////////////////////////////////////////////////////// 1285 // help 1286 ////////////////////////////////////////////////////////////////////////// 1287 protected static String executeHelp(String mdxCmd) { 1288 StringBuilder buf = new StringBuilder(200); 1289 1290 String[] tokens = mdxCmd.split("\\s+"); 1291 1292 int cmd = UNKNOWN_CMD; 1293 1294 if (tokens.length == 1) { 1295 buf.append("Commands:"); 1296 cmd = ALL_CMD; 1297 1298 } else if (tokens.length == 2) { 1299 String cmdName = tokens[1]; 1300 1301 if (cmdName.equals("help")) { 1302 cmd = HELP_CMD; 1303 } else if (cmdName.equals("set")) { 1304 cmd = SET_CMD; 1305 } else if (cmdName.equals("log")) { 1306 cmd = LOG_CMD; 1307 } else if (cmdName.equals("file")) { 1308 cmd = FILE_CMD; 1309 } else if (cmdName.equals("list")) { 1310 cmd = LIST_CMD; 1311 } else if (cmdName.equals("func")) { 1312 cmd = FUNC_CMD; 1313 } else if (cmdName.equals("param")) { 1314 cmd = PARAM_CMD; 1315 } else if (cmdName.equals("cube")) { 1316 cmd = CUBE_CMD; 1317 } else if (cmdName.equals("error")) { 1318 cmd = ERROR_CMD; 1319 } else if (cmdName.equals("echo")) { 1320 cmd = ECHO_CMD; 1321 } else if (cmdName.equals("exit")) { 1322 cmd = EXIT_CMD; 1323 } else { 1324 cmd = UNKNOWN_CMD; 1325 } 1326 } 1327 1328 if (cmd == UNKNOWN_CMD) { 1329 buf.append("Unknown help command: "); 1330 buf.append(mdxCmd); 1331 buf.append(nl); 1332 buf.append("Type \"help\" for list of commands"); 1333 } 1334 1335 if ((cmd & HELP_CMD) != 0) { 1336 // help 1337 buf.append(nl); 1338 appendIndent(buf, 1); 1339 buf.append("help"); 1340 buf.append(nl); 1341 appendIndent(buf, 2); 1342 buf.append("Prints this text"); 1343 } 1344 1345 if ((cmd & SET_CMD) != 0) { 1346 // set 1347 buf.append(nl); 1348 appendSet(buf); 1349 } 1350 1351 if ((cmd & LOG_CMD) != 0) { 1352 // set 1353 buf.append(nl); 1354 appendLog(buf); 1355 } 1356 1357 if ((cmd & FILE_CMD) != 0) { 1358 // file 1359 buf.append(nl); 1360 appendFile(buf); 1361 1362 } 1363 if ((cmd & LIST_CMD) != 0) { 1364 // list 1365 buf.append(nl); 1366 appendList(buf); 1367 } 1368 1369 if ((cmd & MDX_CMD) != 0) { 1370 buf.append(nl); 1371 appendIndent(buf, 1); 1372 buf.append("<mdx query> <cr> ( '"); 1373 buf.append(EXECUTE_CHAR); 1374 buf.append("' | '"); 1375 buf.append(CANCEL_CHAR); 1376 buf.append("' ) <cr>"); 1377 buf.append(nl); 1378 appendIndent(buf, 2); 1379 buf.append("Execute or cancel mdx query."); 1380 buf.append(nl); 1381 appendIndent(buf, 2); 1382 buf.append("An mdx query may span one or more lines."); 1383 buf.append(nl); 1384 appendIndent(buf, 2); 1385 buf.append("After the last line of the query has been entered,"); 1386 buf.append(nl); 1387 appendIndent(buf, 3); 1388 buf.append("on the next line a single execute character, '"); 1389 buf.append(EXECUTE_CHAR); 1390 buf.append("', may be entered"); 1391 buf.append(nl); 1392 appendIndent(buf, 3); 1393 buf.append("followed by a carriage return."); 1394 buf.append(nl); 1395 appendIndent(buf, 3); 1396 buf.append("The lone '"); 1397 buf.append(EXECUTE_CHAR); 1398 buf.append("' informs the interpreter that the query has"); 1399 buf.append(nl); 1400 appendIndent(buf, 3); 1401 buf.append("has been entered and is ready to execute."); 1402 buf.append(nl); 1403 appendIndent(buf, 2); 1404 buf.append("At anytime during the entry of a query the cancel"); 1405 buf.append(nl); 1406 appendIndent(buf, 3); 1407 buf.append("character, '"); 1408 buf.append(CANCEL_CHAR); 1409 buf.append("', may be entered alone on a line."); 1410 buf.append(nl); 1411 appendIndent(buf, 3); 1412 buf.append("This removes all of the query text from the"); 1413 buf.append(nl); 1414 appendIndent(buf, 3); 1415 buf.append("the command interpreter."); 1416 buf.append(nl); 1417 appendIndent(buf, 2); 1418 buf.append("Queries can also be ended by using a semicolon ';'"); 1419 buf.append(nl); 1420 appendIndent(buf, 3); 1421 buf.append("at the end of a line."); 1422 } 1423 if ((cmd & FUNC_CMD) != 0) { 1424 buf.append(nl); 1425 appendFunc(buf); 1426 } 1427 1428 if ((cmd & PARAM_CMD) != 0) { 1429 buf.append(nl); 1430 appendParam(buf); 1431 } 1432 1433 if ((cmd & CUBE_CMD) != 0) { 1434 buf.append(nl); 1435 appendCube(buf); 1436 } 1437 1438 if ((cmd & ERROR_CMD) != 0) { 1439 buf.append(nl); 1440 appendError(buf); 1441 } 1442 1443 if ((cmd & ECHO_CMD) != 0) { 1444 buf.append(nl); 1445 appendEcho(buf); 1446 } 1447 1448 if ((cmd & EXPR_CMD) != 0) { 1449 buf.append(nl); 1450 appendExpr(buf); 1451 } 1452 1453 if (cmd == ALL_CMD) { 1454 // reexecute 1455 buf.append(nl); 1456 appendIndent(buf, 1); 1457 buf.append("= <cr>"); 1458 buf.append(nl); 1459 appendIndent(buf, 2); 1460 buf.append("Re-Execute mdx query."); 1461 } 1462 1463 if ((cmd & EXIT_CMD) != 0) { 1464 // exit 1465 buf.append(nl); 1466 appendExit(buf); 1467 } 1468 1469 1470 return buf.toString(); 1471 } 1472 1473 protected static void appendIndent(StringBuilder buf, int i) { 1474 while (i-- > 0) { 1475 buf.append(CmdRunner.INDENT); 1476 } 1477 } 1478 1479 ////////////////////////////////////////////////////////////////////////// 1480 // set 1481 ////////////////////////////////////////////////////////////////////////// 1482 protected static void appendSet(StringBuilder buf) { 1483 appendIndent(buf, 1); 1484 buf.append("set [ property[=value ] ] <cr>"); 1485 buf.append(nl); 1486 appendIndent(buf, 2); 1487 buf.append("With no args, prints all mondrian properties and values."); 1488 buf.append(nl); 1489 appendIndent(buf, 2); 1490 buf.append("With \"property\" prints property's value."); 1491 buf.append(nl); 1492 appendIndent(buf, 2); 1493 buf.append("With \"property=value\" set property to that value."); 1494 } 1495 1496 protected String executeSet(String mdxCmd) { 1497 StringBuilder buf = new StringBuilder(400); 1498 1499 String[] tokens = mdxCmd.split("\\s+"); 1500 1501 if (tokens.length == 1) { 1502 // list all properties 1503 listPropertiesAll(buf); 1504 1505 } else if (tokens.length == 2) { 1506 String arg = tokens[1]; 1507 int index = arg.indexOf('='); 1508 if (index == -1) { 1509 listProperty(arg, buf); 1510 } else { 1511 String[] nv = arg.split("="); 1512 String name = nv[0]; 1513 String value = nv[1]; 1514 if (isProperty(name)) { 1515 try { 1516 if (setProperty(name, value)) { 1517 this.connectString = null; 1518 } 1519 } catch (Exception ex) { 1520 setError(ex); 1521 } 1522 } else { 1523 buf.append("Bad property name:"); 1524 buf.append(name); 1525 buf.append(nl); 1526 } 1527 } 1528 1529 } else { 1530 buf.append("Bad command usage: \""); 1531 buf.append(mdxCmd); 1532 buf.append('"'); 1533 buf.append(nl); 1534 appendSet(buf); 1535 } 1536 1537 return buf.toString(); 1538 } 1539 1540 ////////////////////////////////////////////////////////////////////////// 1541 // log 1542 ////////////////////////////////////////////////////////////////////////// 1543 protected static void appendLog(StringBuilder buf) { 1544 appendIndent(buf, 1); 1545 buf.append("log [ classname[=level ] ] <cr>"); 1546 buf.append(nl); 1547 appendIndent(buf, 2); 1548 buf.append("With no args, prints the current log level of all classes."); 1549 buf.append(nl); 1550 appendIndent(buf, 2); 1551 buf.append("With \"classname\" prints the current log level of the class."); 1552 buf.append(nl); 1553 appendIndent(buf, 2); 1554 buf.append("With \"classname=level\" set log level to new value."); 1555 } 1556 1557 protected String executeLog(String mdxCmd) { 1558 StringBuilder buf = new StringBuilder(200); 1559 1560 String[] tokens = mdxCmd.split("\\s+"); 1561 1562 if (tokens.length == 1) { 1563 Enumeration e = LogManager.getCurrentLoggers(); 1564 while (e.hasMoreElements()) { 1565 Logger logger = (Logger) e.nextElement(); 1566 buf.append(logger.getName()); 1567 buf.append(':'); 1568 buf.append(logger.getLevel()); 1569 buf.append(nl); 1570 } 1571 1572 } else if (tokens.length == 2) { 1573 String arg = tokens[1]; 1574 int index = arg.indexOf('='); 1575 if (index == -1) { 1576 Logger logger = LogManager.exists(arg); 1577 if (logger == null) { 1578 buf.append("Bad log name: "); 1579 buf.append(arg); 1580 buf.append(nl); 1581 } else { 1582 buf.append(logger.getName()); 1583 buf.append(':'); 1584 buf.append(logger.getLevel()); 1585 buf.append(nl); 1586 } 1587 } else { 1588 String[] nv = arg.split("="); 1589 String classname = nv[0]; 1590 String levelStr = nv[1]; 1591 1592 Logger logger = LogManager.getLogger(classname); 1593 1594 if (logger == null) { 1595 buf.append("Bad log name: "); 1596 buf.append(classname); 1597 buf.append(nl); 1598 } else { 1599 Level level = Level.toLevel(levelStr, null); 1600 if (level == null) { 1601 buf.append("Bad log level: "); 1602 buf.append(levelStr); 1603 buf.append(nl); 1604 } else { 1605 logger.setLevel(level); 1606 } 1607 } 1608 } 1609 1610 } else { 1611 buf.append("Bad command usage: \""); 1612 buf.append(mdxCmd); 1613 buf.append('"'); 1614 buf.append(nl); 1615 appendSet(buf); 1616 } 1617 1618 return buf.toString(); 1619 } 1620 1621 ////////////////////////////////////////////////////////////////////////// 1622 // file 1623 ////////////////////////////////////////////////////////////////////////// 1624 protected static void appendFile(StringBuilder buf) { 1625 appendIndent(buf, 1); 1626 buf.append("file [ filename | '=' ] <cr>"); 1627 buf.append(nl); 1628 appendIndent(buf, 2); 1629 buf.append("With no args, prints the last filename executed."); 1630 buf.append(nl); 1631 appendIndent(buf, 2); 1632 buf.append("With \"filename\", read and execute filename ."); 1633 buf.append(nl); 1634 appendIndent(buf, 2); 1635 buf.append("With \"=\" character, re-read and re-execute previous filename ."); 1636 } 1637 1638 protected String executeFile(String mdxCmd) { 1639 StringBuilder buf = new StringBuilder(512); 1640 String[] tokens = mdxCmd.split("\\s+"); 1641 1642 if (tokens.length == 1) { 1643 if (this.filename != null) { 1644 buf.append(this.filename); 1645 } 1646 1647 } else if (tokens.length == 2) { 1648 String token = tokens[1]; 1649 String nameOfFile = null; 1650 if ((token.length() == 1) && (token.charAt(0) == EXECUTE_CHAR)) { 1651 // file '=' 1652 if (this.filename == null) { 1653 buf.append("Bad command usage: \""); 1654 buf.append(mdxCmd); 1655 buf.append("\", no file to re-execute"); 1656 buf.append(nl); 1657 appendFile(buf); 1658 } else { 1659 nameOfFile = this.filename; 1660 } 1661 } else { 1662 // file filename 1663 nameOfFile = token; 1664 } 1665 1666 if (nameOfFile != null) { 1667 this.filename = nameOfFile; 1668 1669 try { 1670 commandLoop(new File(this.filename)); 1671 } catch (IOException ex) { 1672 setError(ex); 1673 buf.append("Error: ").append(ex); 1674 } 1675 } 1676 1677 } else { 1678 buf.append("Bad command usage: \""); 1679 buf.append(mdxCmd); 1680 buf.append('"'); 1681 buf.append(nl); 1682 appendFile(buf); 1683 } 1684 return buf.toString(); 1685 } 1686 1687 ////////////////////////////////////////////////////////////////////////// 1688 // list 1689 ////////////////////////////////////////////////////////////////////////// 1690 protected static void appendList(StringBuilder buf) { 1691 appendIndent(buf, 1); 1692 buf.append("list [ cmd | result ] <cr>"); 1693 buf.append(nl); 1694 appendIndent(buf, 2); 1695 buf.append("With no arguments, list previous cmd and result"); 1696 buf.append(nl); 1697 appendIndent(buf, 2); 1698 buf.append("With \"cmd\" argument, list the last mdx query cmd."); 1699 buf.append(nl); 1700 appendIndent(buf, 2); 1701 buf.append("With \"result\" argument, list the last mdx query result."); 1702 } 1703 1704 protected String executeList(String mdxCmd) { 1705 StringBuilder buf = new StringBuilder(200); 1706 1707 String[] tokens = mdxCmd.split("\\s+"); 1708 1709 if (tokens.length == 1) { 1710 if (this.mdxCmd != null) { 1711 buf.append(this.mdxCmd); 1712 if (mdxResult != null) { 1713 buf.append(nl); 1714 buf.append(mdxResult); 1715 } 1716 } else if (mdxResult != null) { 1717 buf.append(mdxResult); 1718 } 1719 1720 } else if (tokens.length == 2) { 1721 String arg = tokens[1]; 1722 if (arg.equals("cmd")) { 1723 if (this.mdxCmd != null) { 1724 buf.append(this.mdxCmd); 1725 } 1726 } else if (arg.equals("result")) { 1727 if (mdxResult != null) { 1728 buf.append(mdxResult); 1729 } 1730 } else { 1731 buf.append("Bad sub command usage:"); 1732 buf.append(mdxCmd); 1733 buf.append(nl); 1734 appendList(buf); 1735 } 1736 } else { 1737 buf.append("Bad command usage: \""); 1738 buf.append(mdxCmd); 1739 buf.append('"'); 1740 buf.append(nl); 1741 appendList(buf); 1742 } 1743 1744 return buf.toString(); 1745 } 1746 1747 ////////////////////////////////////////////////////////////////////////// 1748 // func 1749 ////////////////////////////////////////////////////////////////////////// 1750 protected static void appendFunc(StringBuilder buf) { 1751 appendIndent(buf, 1); 1752 buf.append("func [ name ] <cr>"); 1753 buf.append(nl); 1754 appendIndent(buf, 2); 1755 buf.append("With no arguments, list all defined function names"); 1756 buf.append(nl); 1757 appendIndent(buf, 2); 1758 buf.append("With \"name\" argument, display the functions:"); 1759 buf.append(nl); 1760 appendIndent(buf, 3); 1761 buf.append("name, description, and syntax"); 1762 } 1763 protected String executeFunc(String mdxCmd) { 1764 StringBuilder buf = new StringBuilder(200); 1765 1766 String[] tokens = mdxCmd.split("\\s+"); 1767 1768 final FunTable funTable = getConnection().getSchema().getFunTable(); 1769 if (tokens.length == 1) { 1770 // prints names only once 1771 List<FunInfo> funInfoList = funTable.getFunInfoList(); 1772 Iterator<FunInfo> it = funInfoList.iterator(); 1773 String prevName = null; 1774 while (it.hasNext()) { 1775 FunInfo fi = it.next(); 1776 String name = fi.getName(); 1777 if (prevName == null || ! prevName.equals(name)) { 1778 buf.append(name); 1779 buf.append(nl); 1780 prevName = name; 1781 } 1782 } 1783 1784 } else if (tokens.length == 2) { 1785 String funcname = tokens[1]; 1786 List<FunInfo> funInfoList = funTable.getFunInfoList(); 1787 List<FunInfo> matches = new ArrayList<FunInfo>(); 1788 1789 for (FunInfo fi : funInfoList) { 1790 if (fi.getName().equalsIgnoreCase(funcname)) { 1791 matches.add(fi); 1792 } 1793 } 1794 1795 if (matches.size() == 0) { 1796 buf.append("Bad function name \""); 1797 buf.append(funcname); 1798 buf.append("\", usage:"); 1799 buf.append(nl); 1800 appendList(buf); 1801 } else { 1802 Iterator<FunInfo> it = matches.iterator(); 1803 boolean doname = true; 1804 while (it.hasNext()) { 1805 FunInfo fi = it.next(); 1806 if (doname) { 1807 buf.append(fi.getName()); 1808 buf.append(nl); 1809 doname = false; 1810 } 1811 1812 appendIndent(buf, 1); 1813 buf.append(fi.getDescription()); 1814 buf.append(nl); 1815 1816 String[] sigs = fi.getSignatures(); 1817 if (sigs == null) { 1818 appendIndent(buf, 2); 1819 buf.append("Signature: "); 1820 buf.append("NONE"); 1821 buf.append(nl); 1822 } else { 1823 for (String sig : sigs) { 1824 appendIndent(buf, 2); 1825 buf.append(sig); 1826 buf.append(nl); 1827 } 1828 } 1829 /* 1830 appendIndent(buf, 1); 1831 buf.append("Return Type: "); 1832 int returnType = fi.getReturnTypes(); 1833 if (returnType >= 0) { 1834 buf.append(cat.getName(returnType)); 1835 } else { 1836 buf.append("NONE"); 1837 } 1838 buf.append(nl); 1839 int[][] paramsArray = fi.getParameterTypes(); 1840 if (paramsArray == null) { 1841 appendIndent(buf, 1); 1842 buf.append("Paramter Types: "); 1843 buf.append("NONE"); 1844 buf.append(nl); 1845 1846 } else { 1847 1848 for (int j = 0; j < paramsArray.length; j++) { 1849 int[] params = paramsArray[j]; 1850 appendIndent(buf, 1); 1851 buf.append("Paramter Types: "); 1852 for (int k = 0; k < params.length; k++) { 1853 int param = params[k]; 1854 buf.append(cat.getName(param)); 1855 buf.append(' '); 1856 } 1857 buf.append(nl); 1858 } 1859 } 1860 */ 1861 } 1862 } 1863 } else { 1864 buf.append("Bad command usage: \""); 1865 buf.append(mdxCmd); 1866 buf.append('"'); 1867 buf.append(nl); 1868 appendList(buf); 1869 } 1870 1871 return buf.toString(); 1872 } 1873 ////////////////////////////////////////////////////////////////////////// 1874 // param 1875 ////////////////////////////////////////////////////////////////////////// 1876 protected static void appendParam(StringBuilder buf) { 1877 appendIndent(buf, 1); 1878 buf.append("param [ name[=value ] ] <cr>"); 1879 buf.append(nl); 1880 appendIndent(buf, 2); 1881 buf.append("With no argumnts, all param name/value pairs are printed."); 1882 buf.append(nl); 1883 appendIndent(buf, 2); 1884 buf.append("With \"name\" argument, the value of the param is printed."); 1885 buf.append(nl); 1886 appendIndent(buf, 2); 1887 buf.append("With \"name=value\" sets the parameter with name to value."); 1888 buf.append(nl); 1889 appendIndent(buf, 3); 1890 buf.append(" If name is null, then unsets all parameters"); 1891 buf.append(nl); 1892 appendIndent(buf, 3); 1893 buf.append(" If value is null, then unsets the parameter associated with value"); 1894 } 1895 protected String executeParam(String mdxCmd) { 1896 StringBuilder buf = new StringBuilder(200); 1897 1898 String[] tokens = mdxCmd.split("\\s+"); 1899 1900 if (tokens.length == 1) { 1901 // list all properties 1902 listParameterNameValues(buf); 1903 1904 } else if (tokens.length == 2) { 1905 String arg = tokens[1]; 1906 int index = arg.indexOf('='); 1907 if (index == -1) { 1908 if (isParam(arg)) { 1909 listParam(arg, buf); 1910 } else { 1911 buf.append("Bad parameter name:"); 1912 buf.append(arg); 1913 buf.append(nl); 1914 } 1915 } else { 1916 String[] nv = arg.split("="); 1917 String name = (nv.length == 0) ? null : nv[0]; 1918 String value = (nv.length == 2) ? nv[1] : null; 1919 setParameter(name, value); 1920 } 1921 1922 } else { 1923 buf.append("Bad command usage: \""); 1924 buf.append(mdxCmd); 1925 buf.append('"'); 1926 buf.append(nl); 1927 appendSet(buf); 1928 } 1929 1930 return buf.toString(); 1931 } 1932 ////////////////////////////////////////////////////////////////////////// 1933 // cube 1934 ////////////////////////////////////////////////////////////////////////// 1935 protected static void appendCube(StringBuilder buf) { 1936 appendIndent(buf, 1); 1937 buf.append("cube [ cubename [ name [=value | command] ] ] <cr>"); 1938 buf.append(nl); 1939 appendIndent(buf, 2); 1940 buf.append("With no argumnts, all cubes are listed by name."); 1941 buf.append(nl); 1942 appendIndent(buf, 2); 1943 buf.append("With \"cubename\" argument, cube attribute name/values for:"); 1944 buf.append(nl); 1945 appendIndent(buf, 3); 1946 buf.append("fact table (readonly)"); 1947 buf.append(nl); 1948 appendIndent(buf, 3); 1949 buf.append("aggregate caching (readwrite)"); 1950 buf.append(nl); 1951 appendIndent(buf, 2); 1952 buf.append("are printed"); 1953 buf.append(nl); 1954 appendIndent(buf, 2); 1955 buf.append("With \"cubename name=value\" sets the readwrite attribute with name to value."); 1956 buf.append(nl); 1957 appendIndent(buf, 2); 1958 buf.append("With \"cubename command\" executes the commands:"); 1959 buf.append(nl); 1960 appendIndent(buf, 3); 1961 buf.append("clearCache"); 1962 } 1963 1964 protected String executeCube(String mdxCmd) { 1965 StringBuilder buf = new StringBuilder(200); 1966 1967 String[] tokens = mdxCmd.split("\\s+"); 1968 1969 if (tokens.length == 1) { 1970 // list all properties 1971 listCubeName(buf); 1972 } else if (tokens.length == 2) { 1973 String cubename = tokens[1]; 1974 listCubeAttribues(cubename, buf); 1975 1976 } else if (tokens.length == 3) { 1977 String cubename = tokens[1]; 1978 String arg = tokens[2]; 1979 int index = arg.indexOf('='); 1980 if (index == -1) { 1981 // its a commnd 1982 executeCubeCommand(cubename, arg, buf); 1983 } else { 1984 String[] nv = arg.split("="); 1985 String name = (nv.length == 0) ? null : nv[0]; 1986 String value = (nv.length == 2) ? nv[1] : null; 1987 setCubeAttribute(cubename, name, value, buf); 1988 } 1989 1990 } else { 1991 buf.append("Bad command usage: \""); 1992 buf.append(mdxCmd); 1993 buf.append('"'); 1994 buf.append(nl); 1995 appendSet(buf); 1996 } 1997 1998 return buf.toString(); 1999 } 2000 ////////////////////////////////////////////////////////////////////////// 2001 // error 2002 ////////////////////////////////////////////////////////////////////////// 2003 protected static void appendError(StringBuilder buf) { 2004 appendIndent(buf, 1); 2005 buf.append("error [ msg | stack ] <cr>"); 2006 buf.append(nl); 2007 appendIndent(buf, 2); 2008 buf.append("With no argumnts, both message and stack are printed."); 2009 buf.append(nl); 2010 appendIndent(buf, 2); 2011 buf.append("With \"msg\" argument, the Error message is printed."); 2012 buf.append(nl); 2013 appendIndent(buf, 2); 2014 buf.append("With \"stack\" argument, the Error stack trace is printed."); 2015 } 2016 2017 protected String executeError(String mdxCmd) { 2018 StringBuilder buf = new StringBuilder(200); 2019 2020 String[] tokens = mdxCmd.split("\\s+"); 2021 2022 if (tokens.length == 1) { 2023 if (error != null) { 2024 buf.append(error); 2025 if (stack != null) { 2026 buf.append(nl); 2027 buf.append(stack); 2028 } 2029 } else if (stack != null) { 2030 buf.append(stack); 2031 } 2032 2033 } else if (tokens.length == 2) { 2034 String arg = tokens[1]; 2035 if (arg.equals("msg")) { 2036 if (error != null) { 2037 buf.append(error); 2038 } 2039 } else if (arg.equals("stack")) { 2040 if (stack != null) { 2041 buf.append(stack); 2042 } 2043 } else { 2044 buf.append("Bad sub command usage:"); 2045 buf.append(mdxCmd); 2046 buf.append(nl); 2047 appendList(buf); 2048 } 2049 } else { 2050 buf.append("Bad command usage: \""); 2051 buf.append(mdxCmd); 2052 buf.append('"'); 2053 buf.append(nl); 2054 appendList(buf); 2055 } 2056 2057 return buf.toString(); 2058 } 2059 ////////////////////////////////////////////////////////////////////////// 2060 // echo 2061 ////////////////////////////////////////////////////////////////////////// 2062 protected static void appendEcho(StringBuilder buf) { 2063 appendIndent(buf, 1); 2064 buf.append("echo text <cr>"); 2065 buf.append(nl); 2066 appendIndent(buf, 2); 2067 buf.append("echo text to standard out."); 2068 } 2069 protected String executeEcho(String mdxCmd) { 2070 2071 try { 2072 String resultString = (mdxCmd.length() == 4) 2073 ? "" : mdxCmd.substring(4); 2074 return resultString; 2075 2076 } catch (Exception ex) { 2077 setError(ex); 2078 //return error; 2079 return null; 2080 } 2081 } 2082 ////////////////////////////////////////////////////////////////////////// 2083 // expr 2084 ////////////////////////////////////////////////////////////////////////// 2085 protected static void appendExpr(StringBuilder buf) { 2086 appendIndent(buf, 1); 2087 buf.append("expr cubename expression<cr>"); 2088 buf.append(nl); 2089 appendIndent(buf, 2); 2090 buf.append("evaluate an expression against a cube."); 2091 buf.append(nl); 2092 appendIndent(buf, 2); 2093 buf.append("where: "); 2094 buf.append(nl); 2095 appendIndent(buf, 3); 2096 buf.append("cubename is single word or string using [], '' or \"\""); 2097 buf.append(nl); 2098 appendIndent(buf, 3); 2099 buf.append("expression is string using \"\""); 2100 } 2101 protected String executeExpr(String mdxCmd) { 2102 StringBuilder buf = new StringBuilder(256); 2103 2104 mdxCmd = (mdxCmd.length() == 5) 2105 ? "" : mdxCmd.substring(5); 2106 2107 String regex = "(\"[^\"]+\"|'[^\']+'|\\[[^\\]]+\\]|[^\\s]+)\\s+.*"; 2108 Pattern p = Pattern.compile(regex); 2109 Matcher m = p.matcher(mdxCmd); 2110 boolean b = m.matches(); 2111 2112 if (! b) { 2113 buf.append("Could not parse into \"cubename expression\" command:"); 2114 buf.append(nl); 2115 buf.append(mdxCmd); 2116 String msg = buf.toString(); 2117 setError(msg); 2118 return msg; 2119 } else { 2120 2121 String cubeName = m.group(1); 2122 String expression = mdxCmd.substring(cubeName.length() + 1); 2123 2124 if (cubeName.charAt(0) == '"') { 2125 cubeName = cubeName.substring(1, cubeName.length() - 1); 2126 } else if (cubeName.charAt(0) == '\'') { 2127 cubeName = cubeName.substring(1, cubeName.length() - 1); 2128 } else if (cubeName.charAt(0) == '[') { 2129 cubeName = cubeName.substring(1, cubeName.length() - 1); 2130 } 2131 2132 int len = expression.length(); 2133 if (expression.charAt(0) == '"') { 2134 if (expression.charAt(len - 1) != '"') { 2135 buf.append("Missing end '\"' in expression:"); 2136 buf.append(nl); 2137 buf.append(expression); 2138 String msg = buf.toString(); 2139 setError(msg); 2140 return msg; 2141 } 2142 expression = expression.substring(1, len - 1); 2143 2144 } else if (expression.charAt(0) == '\'') { 2145 if (expression.charAt(len - 1) != '\'') { 2146 buf.append("Missing end \"'\" in expression:"); 2147 buf.append(nl); 2148 buf.append(expression); 2149 String msg = buf.toString(); 2150 setError(msg); 2151 return msg; 2152 } 2153 expression = expression.substring(1, len - 1); 2154 } 2155 2156 Cube cube = getCube(cubeName); 2157 if (cube == null) { 2158 buf.append("No cube found with name \""); 2159 buf.append(cubeName); 2160 buf.append("\""); 2161 String msg = buf.toString(); 2162 setError(msg); 2163 return msg; 2164 2165 } else { 2166 try { 2167 if (cubeName.indexOf(' ') >= 0) { 2168 if (cubeName.charAt(0) != '[') { 2169 cubeName = Util.quoteMdxIdentifier(cubeName); 2170 } 2171 } 2172 final char c = '\''; 2173 if (expression.indexOf('\'') != -1) { 2174 // make sure all "'" are escaped 2175 int start = 0; 2176 int index = expression.indexOf('\'', start); 2177 if (index == 0) { 2178 // error: starts with "'" 2179 buf.append("Double \"''\" starting expression:"); 2180 buf.append(nl); 2181 buf.append(expression); 2182 String msg = buf.toString(); 2183 setError(msg); 2184 return msg; 2185 } 2186 while (index != -1) { 2187 if (expression.charAt(index - 1) != '\\') { 2188 // error 2189 buf.append("Non-escaped \"'\" in expression:"); 2190 buf.append(nl); 2191 buf.append(expression); 2192 String msg = buf.toString(); 2193 setError(msg); 2194 return msg; 2195 } 2196 start = index + 1; 2197 index = expression.indexOf('\'', start); 2198 } 2199 } 2200 2201 // taken from FoodMartTest code 2202 StringBuilder queryStringBuf = new StringBuilder(64); 2203 queryStringBuf.append("with member [Measures].[Foo] as "); 2204 queryStringBuf.append(c); 2205 queryStringBuf.append(expression); 2206 queryStringBuf.append(c); 2207 queryStringBuf.append(" select {[Measures].[Foo]} on columns from "); 2208 queryStringBuf.append(cubeName); 2209 2210 String queryString = queryStringBuf.toString(); 2211 2212 Result result = runQuery(queryString, true); 2213 String resultString = 2214 result.getCell(new int[]{0}).getFormattedValue(); 2215 mdxResult = resultString; 2216 clearError(); 2217 2218 buf.append(resultString); 2219 2220 } catch (Exception ex) { 2221 setError(ex); 2222 buf.append("Error: ").append(ex); 2223 } 2224 } 2225 } 2226 return buf.toString(); 2227 } 2228 ////////////////////////////////////////////////////////////////////////// 2229 // exit 2230 ////////////////////////////////////////////////////////////////////////// 2231 protected static void appendExit(StringBuilder buf) { 2232 appendIndent(buf, 1); 2233 buf.append("exit <cr>"); 2234 buf.append(nl); 2235 appendIndent(buf, 2); 2236 buf.append("Exit mdx command interpreter."); 2237 } 2238 2239 2240 protected String reExecuteMdxCmd() { 2241 if (this.mdxCmd == null) { 2242 return "No command to execute"; 2243 } else { 2244 return executeMdxCmd(this.mdxCmd); 2245 } 2246 } 2247 2248 protected String executeMdxCmd(String mdxCmd) { 2249 2250 this.mdxCmd = mdxCmd; 2251 try { 2252 2253 String resultString = execute(mdxCmd); 2254 mdxResult = resultString; 2255 clearError(); 2256 return resultString; 2257 2258 } catch (Exception ex) { 2259 setError(ex); 2260 //return error; 2261 return null; 2262 } 2263 } 2264 2265 ///////////////////////////////////////////////////////////////////////// 2266 // helpers 2267 ///////////////////////////////////////////////////////////////////////// 2268 protected static void loadPropertiesFromFile(String propFile) 2269 throws IOException { 2270 2271 MondrianProperties.instance().load(new FileInputStream(propFile)); 2272 } 2273 2274 ///////////////////////////////////////////////////////////////////////// 2275 // main 2276 ///////////////////////////////////////////////////////////////////////// 2277 2278 /** 2279 * Prints a usage message. 2280 * 2281 * @param msg Prefix to the message 2282 * @param out Output stream 2283 */ 2284 protected static void usage(String msg, PrintStream out) { 2285 StringBuilder buf = new StringBuilder(256); 2286 if (msg != null) { 2287 buf.append(msg); 2288 buf.append(nl); 2289 } 2290 buf.append("Usage: mondrian.tui.CmdRunner args"); 2291 buf.append(nl); 2292 buf.append(" args:"); 2293 buf.append(nl); 2294 buf.append(" -h : print this usage text"); 2295 buf.append(nl); 2296 buf.append(" -H : ready to print out high cardinality"); 2297 buf.append(nl); 2298 buf.append(" dimensions"); 2299 buf.append(nl); 2300 buf.append(" -d : enable local debugging"); 2301 buf.append(nl); 2302 buf.append(" -t : time each mdx query"); 2303 buf.append(nl); 2304 buf.append(" -nocache : turn off in-memory aggregate caching"); 2305 buf.append(nl); 2306 buf.append(" for all cubes regardless of setting"); 2307 buf.append(nl); 2308 buf.append(" in schema"); 2309 buf.append(nl); 2310 buf.append(" -rc : do NOT reload connections each query"); 2311 buf.append(nl); 2312 buf.append(" (default is to reload connections)"); 2313 buf.append(nl); 2314 buf.append(" -p propertyfile : load mondrian properties"); 2315 buf.append(nl); 2316 buf.append(" -r role_name : set the connections role name"); 2317 buf.append(nl); 2318 buf.append(" -f mdx_filename+ : execute mdx in one or more files"); 2319 buf.append(nl); 2320 buf.append(" -x xmla_filename+: execute XMLA in one or more files"); 2321 buf.append(" the XMLA request has no SOAP wrapper"); 2322 buf.append(nl); 2323 buf.append(" -xs soap_xmla_filename+ "); 2324 buf.append(" : execute Soap XMLA in one or more files"); 2325 buf.append(" the XMLA request has a SOAP wrapper"); 2326 buf.append(nl); 2327 buf.append(" -vt : validate xmla response using transforms"); 2328 buf.append(" only used with -x or -xs flags"); 2329 buf.append(nl); 2330 buf.append(" -vx : validate xmla response using xpaths"); 2331 buf.append(" only used with -x or -xs flags"); 2332 buf.append(nl); 2333 buf.append(" mdx_cmd : execute mdx_cmd"); 2334 buf.append(nl); 2335 2336 out.println(buf.toString()); 2337 } 2338 2339 /** 2340 * Set the default comment delimiters for CmdRunner. These defaults are 2341 * # to end of line 2342 * plus all the comment delimiters in Scanner. 2343 */ 2344 private static void setDefaultCommentState() { 2345 allowNestedComments = mondrian.olap.Scanner.getNestedCommentsState(); 2346 String[][] scannerCommentsDelimiters = mondrian.olap.Scanner.getCommentDelimiters(); 2347 commentDelim = new String[scannerCommentsDelimiters.length + 1][2]; 2348 commentStartChars = new char[scannerCommentsDelimiters.length + 1]; 2349 2350 2351 // CmdRunner has extra delimiter; # to end of line 2352 commentDelim[0][0] = "#"; 2353 commentDelim[0][1] = null; 2354 commentStartChars[0] = commentDelim[0][0].charAt(0); 2355 2356 2357 // copy all the rest of the delimiters 2358 for (int x = 0; x < scannerCommentsDelimiters.length; x++) { 2359 commentDelim[x + 1][0] = scannerCommentsDelimiters[x][0]; 2360 commentDelim[x + 1][1] = scannerCommentsDelimiters[x][1]; 2361 commentStartChars[x + 1] = commentDelim[x + 1][0].charAt(0); 2362 } 2363 } 2364 2365 private static final int DO_MDX = 1; 2366 private static final int DO_XMLA = 2; 2367 private static final int DO_SOAP_XMLA = 3; 2368 2369 private static final int VALIDATE_NONE = 1; 2370 private static final int VALIDATE_TRANSFORM = 2; 2371 private static final int VALIDATE_XPATH = 3; 2372 2373 protected static class Options { 2374 private boolean debug = false; 2375 private boolean timeQueries; 2376 private boolean noCache = false; 2377 private String roleName; 2378 private int validateXmlaResponse = VALIDATE_NONE; 2379 private final List<String> filenames = new ArrayList<String>(); 2380 private int doingWhat = DO_MDX; 2381 private String singleMdxCmd; 2382 private boolean highCardResults; 2383 } 2384 2385 public static void main(String[] args) throws Exception { 2386 2387 Options options; 2388 try { 2389 options = parseOptions(args); 2390 } catch (BadOption badOption) { 2391 usage(badOption.getMessage(), System.out); 2392 Throwable t = badOption.getCause(); 2393 if (t != null) { 2394 System.out.println(t); 2395 t.printStackTrace(); 2396 } 2397 return; 2398 } 2399 2400 CmdRunner cmdRunner = 2401 new CmdRunner(options, new PrintWriter(System.out)); 2402 if (options.noCache) { 2403 cmdRunner.noCubeCaching(); 2404 } 2405 2406 if (!options.filenames.isEmpty()) { 2407 for (String filename : options.filenames) { 2408 cmdRunner.filename = filename; 2409 switch (options.doingWhat) { 2410 case DO_MDX: 2411 // its a file containing mdx 2412 cmdRunner.commandLoop(new File(filename)); 2413 break; 2414 case DO_XMLA: 2415 // its a file containing XMLA 2416 cmdRunner.processXmla( 2417 new File(filename), 2418 options.validateXmlaResponse); 2419 break; 2420 default: 2421 // its a file containing SOAP XMLA 2422 cmdRunner.processSoapXmla( 2423 new File(filename), 2424 options.validateXmlaResponse); 2425 break; 2426 } 2427 if (cmdRunner.error != null) { 2428 System.err.println(filename); 2429 System.err.println(cmdRunner.error); 2430 if (cmdRunner.stack != null) { 2431 System.err.println(cmdRunner.stack); 2432 } 2433 cmdRunner.printQueryTime(); 2434 cmdRunner.clearError(); 2435 } 2436 } 2437 } else if (options.singleMdxCmd != null) { 2438 cmdRunner.commandLoop(options.singleMdxCmd, false); 2439 if (cmdRunner.error != null) { 2440 System.err.println(cmdRunner.error); 2441 if (cmdRunner.stack != null) { 2442 System.err.println(cmdRunner.stack); 2443 } 2444 } 2445 } else { 2446 cmdRunner.commandLoop(true); 2447 } 2448 cmdRunner.printTotalQueryTime(); 2449 } 2450 2451 private void printTotalQueryTime() { 2452 if (options.timeQueries) { 2453 // only print if different 2454 if (totalQueryTime != queryTime) { 2455 out.println("total[" + totalQueryTime + "ms]"); 2456 } 2457 } 2458 out.flush(); 2459 } 2460 2461 private static Options parseOptions(String[] args) 2462 throws BadOption, IOException { 2463 final Options options = new Options(); 2464 for (int i = 0; i < args.length; i++) { 2465 String arg = args[i]; 2466 2467 if (arg.equals("-h")) { 2468 throw new BadOption(null); 2469 } else if (arg.equals("-H")) { 2470 options.highCardResults = true; 2471 2472 } else if (arg.equals("-d")) { 2473 options.debug = true; 2474 2475 } else if (arg.equals("-t")) { 2476 options.timeQueries = true; 2477 2478 } else if (arg.equals("-nocache")) { 2479 options.noCache = true; 2480 2481 } else if (arg.equals("-rc")) { 2482 CmdRunner.RELOAD_CONNECTION = false; 2483 2484 } else if (arg.equals("-vt")) { 2485 options.validateXmlaResponse = VALIDATE_TRANSFORM; 2486 2487 } else if (arg.equals("-vx")) { 2488 options.validateXmlaResponse = VALIDATE_XPATH; 2489 2490 } else if (arg.equals("-f")) { 2491 i++; 2492 if (i == args.length) { 2493 throw new BadOption("no mdx filename given"); 2494 } 2495 options.filenames.add(args[i]); 2496 2497 } else if (arg.equals("-x")) { 2498 i++; 2499 if (i == args.length) { 2500 throw new BadOption("no XMLA filename given"); 2501 } 2502 options.doingWhat = DO_XMLA; 2503 options.filenames.add(args[i]); 2504 2505 } else if (arg.equals("-xs")) { 2506 i++; 2507 if (i == args.length) { 2508 throw new BadOption("no XMLA filename given"); 2509 } 2510 options.doingWhat = DO_SOAP_XMLA; 2511 options.filenames.add(args[i]); 2512 2513 } else if (arg.equals("-p")) { 2514 i++; 2515 if (i == args.length) { 2516 throw new BadOption("no mondrian properties file given"); 2517 } 2518 String propFile = args[i]; 2519 loadPropertiesFromFile(propFile); 2520 2521 } else if (arg.equals("-r")) { 2522 i++; 2523 if (i == args.length) { 2524 throw new BadOption("no role name given"); 2525 } 2526 options.roleName = args[i]; 2527 } else if (!options.filenames.isEmpty()) { 2528 options.filenames.add(arg); 2529 } else { 2530 options.singleMdxCmd = arg; 2531 } 2532 } 2533 return options; 2534 } 2535 2536 private static class BadOption extends Exception { 2537 BadOption(String msg) { 2538 super(msg); 2539 } 2540 BadOption(String msg, Exception ex) { 2541 super(msg, ex); 2542 } 2543 } 2544 } 2545 2546 // End CmdRunner.java 2547