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