001 /* 002 // $Id: //open/mondrian/src/main/mondrian/xmla/impl/DefaultXmlaRequest.java#11 $ 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 007 // All Rights Reserved. 008 // You must accept the terms of that agreement to use this software. 009 */ 010 package mondrian.xmla.impl; 011 012 import java.util.*; 013 014 import mondrian.olap.MondrianProperties; 015 import mondrian.olap.Util; 016 import mondrian.olap.Role; 017 import mondrian.xmla.XmlaConstants; 018 import mondrian.xmla.XmlaException; 019 import mondrian.xmla.XmlaRequest; 020 import mondrian.xmla.XmlaUtil; 021 022 import org.w3c.dom.Element; 023 import org.w3c.dom.Node; 024 import org.w3c.dom.NodeList; 025 import org.apache.log4j.Logger; 026 027 /** 028 * Default implementation of {@link mondrian.xmla.XmlaRequest} by DOM API. 029 * 030 * @author Gang Chen 031 */ 032 public class DefaultXmlaRequest implements XmlaRequest, 033 XmlaConstants { 034 035 private static final Logger LOGGER = Logger.getLogger(DefaultXmlaRequest.class); 036 037 private static final String MSG_INVALID_XMLA = "Invalid XML/A message"; 038 private static final String MSG_INVALID_DRILLTHROUGH = "Invalid DRILLTHROUGH statement"; 039 private static final String MSG_INVALID_MAXROWS = "MAXROWS is not positive integer"; 040 private static final String MSG_INVALID_FIRSTROWSET = "FIRSTROWSET isn't positive integer"; 041 042 /* common content */ 043 private int method; 044 private Map<String, String> properties; 045 private final String roleName; 046 private final Role role; 047 048 /* EXECUTE content */ 049 private String statement; 050 private boolean drillthrough; 051 private int maxRows; 052 private int firstRowset; 053 054 /* DISCOVER contnet */ 055 private String requestType; 056 private Map<String, Object> restrictions; 057 058 059 public DefaultXmlaRequest(final Element xmlaRoot) { 060 this(xmlaRoot, null, null); 061 } 062 063 public DefaultXmlaRequest(final Element xmlaRoot, final String roleName) 064 throws XmlaException { 065 this(xmlaRoot, roleName, null); 066 } 067 068 public DefaultXmlaRequest(final Element xmlaRoot, final Role role) 069 throws XmlaException { 070 this(xmlaRoot, null, role); 071 } 072 protected DefaultXmlaRequest(final Element xmlaRoot, 073 final String roleName, 074 final Role role) 075 throws XmlaException { 076 init(xmlaRoot); 077 this.roleName = roleName; 078 this.role = role; 079 } 080 081 /* Interface implmentation */ 082 083 public int getMethod() { 084 return method; 085 } 086 087 public Map<String, String> getProperties() { 088 return properties; 089 } 090 091 public Map<String, Object> getRestrictions() { 092 if (method != METHOD_DISCOVER) { 093 throw new IllegalStateException("Only METHOD_DISCOVER has restrictions"); 094 } 095 return restrictions; 096 } 097 098 public String getStatement() { 099 if (method != METHOD_EXECUTE) { 100 throw new IllegalStateException("Only METHOD_EXECUTE has statement"); 101 } 102 return statement; 103 } 104 105 public String getRoleName() { 106 return roleName; 107 } 108 109 public Role getRole() { 110 return role; 111 } 112 113 /* 114 public void setRole(String roleName) { 115 this.role = role; 116 } 117 */ 118 119 public String getRequestType() { 120 if (method != METHOD_DISCOVER) 121 throw new IllegalStateException("Only METHOD_DISCOVER has requestType"); 122 return requestType; 123 } 124 125 public boolean isDrillThrough() { 126 if (method != METHOD_EXECUTE) 127 throw new IllegalStateException("Only METHOD_EXECUTE determines drillthrough"); 128 return drillthrough; 129 } 130 131 public int drillThroughMaxRows() { 132 if (method != METHOD_EXECUTE) 133 throw new IllegalStateException("Only METHOD_EXECUTE determines drillthrough"); 134 return maxRows; 135 } 136 137 public int drillThroughFirstRowset() { 138 if (method != METHOD_EXECUTE) 139 throw new IllegalStateException("Only METHOD_EXECUTE determines drillthrough"); 140 return firstRowset; 141 } 142 143 144 protected final void init(Element xmlaRoot) throws XmlaException { 145 if (NS_XMLA.equals(xmlaRoot.getNamespaceURI())) { 146 String lname = xmlaRoot.getLocalName(); 147 if ("Discover".equals(lname)) { 148 method = METHOD_DISCOVER; 149 initDiscover(xmlaRoot); 150 } else if ("Execute".equals(lname)) { 151 method = METHOD_EXECUTE; 152 initExecute(xmlaRoot); 153 } else { 154 // Note that is code will never be reached because 155 // the error will be caught in 156 // DefaultXmlaServlet.handleSoapBody first 157 StringBuilder buf = new StringBuilder(100); 158 buf.append(MSG_INVALID_XMLA); 159 buf.append(": Bad method name \""); 160 buf.append(lname); 161 buf.append("\""); 162 throw new XmlaException( 163 CLIENT_FAULT_FC, 164 HSB_BAD_METHOD_CODE, 165 HSB_BAD_METHOD_FAULT_FS, 166 Util.newError(buf.toString())); 167 } 168 } else { 169 // Note that is code will never be reached because 170 // the error will be caught in 171 // DefaultXmlaServlet.handleSoapBody first 172 StringBuilder buf = new StringBuilder(100); 173 buf.append(MSG_INVALID_XMLA); 174 buf.append(": Bad namespace url \""); 175 buf.append(xmlaRoot.getNamespaceURI()); 176 buf.append("\""); 177 throw new XmlaException( 178 CLIENT_FAULT_FC, 179 HSB_BAD_METHOD_NS_CODE, 180 HSB_BAD_METHOD_NS_FAULT_FS, 181 Util.newError(buf.toString())); 182 } 183 } 184 185 private void initDiscover(Element discoverRoot) throws XmlaException { 186 Element[] childElems = XmlaUtil.filterChildElements(discoverRoot, 187 NS_XMLA, 188 "RequestType"); 189 if (childElems.length != 1) { 190 StringBuilder buf = new StringBuilder(100); 191 buf.append(MSG_INVALID_XMLA); 192 buf.append(": Wrong number of RequestType elements: "); 193 buf.append(childElems.length); 194 throw new XmlaException( 195 CLIENT_FAULT_FC, 196 HSB_BAD_REQUEST_TYPE_CODE, 197 HSB_BAD_REQUEST_TYPE_FAULT_FS, 198 Util.newError(buf.toString())); 199 } 200 requestType = XmlaUtil.textInElement(childElems[0]); // <RequestType> 201 202 childElems = XmlaUtil.filterChildElements(discoverRoot, 203 NS_XMLA, 204 "Restrictions"); 205 if (childElems.length != 1) { 206 StringBuilder buf = new StringBuilder(100); 207 buf.append(MSG_INVALID_XMLA); 208 buf.append(": Wrong number of Restrictions elements: "); 209 buf.append(childElems.length); 210 throw new XmlaException( 211 CLIENT_FAULT_FC, 212 HSB_BAD_RESTRICTIONS_CODE, 213 HSB_BAD_RESTRICTIONS_FAULT_FS, 214 Util.newError(buf.toString())); 215 } 216 initRestrictions(childElems[0]); // <Restriciotns><RestrictionList> 217 218 childElems = XmlaUtil.filterChildElements(discoverRoot, 219 NS_XMLA, 220 "Properties"); 221 if (childElems.length != 1) { 222 StringBuilder buf = new StringBuilder(100); 223 buf.append(MSG_INVALID_XMLA); 224 buf.append(": Wrong number of Properties elements: "); 225 buf.append(childElems.length); 226 throw new XmlaException( 227 CLIENT_FAULT_FC, 228 HSB_BAD_PROPERTIES_CODE, 229 HSB_BAD_PROPERTIES_FAULT_FS, 230 Util.newError(buf.toString())); 231 } 232 initProperties(childElems[0]); // <Properties><PropertyList> 233 } 234 235 private void initExecute(Element executeRoot) throws XmlaException { 236 Element[] childElems = XmlaUtil.filterChildElements(executeRoot, 237 NS_XMLA, 238 "Command"); 239 if (childElems.length != 1) { 240 StringBuilder buf = new StringBuilder(100); 241 buf.append(MSG_INVALID_XMLA); 242 buf.append(": Wrong number of Command elements: "); 243 buf.append(childElems.length); 244 throw new XmlaException( 245 CLIENT_FAULT_FC, 246 HSB_BAD_COMMAND_CODE, 247 HSB_BAD_COMMAND_FAULT_FS, 248 Util.newError(buf.toString())); 249 } 250 initCommand(childElems[0]); // <Command><Statement> 251 252 childElems = XmlaUtil.filterChildElements(executeRoot, 253 NS_XMLA, 254 "Properties"); 255 if (childElems.length != 1) { 256 StringBuilder buf = new StringBuilder(100); 257 buf.append(MSG_INVALID_XMLA); 258 buf.append(": Wrong number of Properties elements: "); 259 buf.append(childElems.length); 260 throw new XmlaException( 261 CLIENT_FAULT_FC, 262 HSB_BAD_PROPERTIES_CODE, 263 HSB_BAD_PROPERTIES_FAULT_FS, 264 Util.newError(buf.toString())); 265 } 266 initProperties(childElems[0]); // <Properties><PropertyList> 267 } 268 269 private void initRestrictions(Element restrictionsRoot) throws XmlaException { 270 Map<String, List<String>> restrictions = new HashMap<String, List<String>>(); 271 Element[] childElems = 272 XmlaUtil.filterChildElements( 273 restrictionsRoot, 274 NS_XMLA, 275 "RestrictionList"); 276 if (childElems.length == 1) { 277 NodeList nlst = childElems[0].getChildNodes(); 278 for (int i = 0, nlen = nlst.getLength(); i < nlen; i++) { 279 Node n = nlst.item(i); 280 if (n instanceof Element) { 281 Element e = (Element) n; 282 if (NS_XMLA.equals(e.getNamespaceURI())) { 283 String key = e.getLocalName(); 284 String value = XmlaUtil.textInElement(e); 285 286 List<String> values; 287 if (restrictions.containsKey(key)) { 288 values = restrictions.get(key); 289 } else { 290 values = new ArrayList<String>(); 291 restrictions.put(key, values); 292 } 293 294 if (LOGGER.isDebugEnabled()) { 295 StringBuilder buf = new StringBuilder(100); 296 buf.append("DefaultXmlaRequest.initRestrictions: "); 297 buf.append(" key=\""); 298 buf.append(key); 299 buf.append("\", value=\""); 300 buf.append(value); 301 buf.append("\""); 302 LOGGER.debug(buf.toString()); 303 } 304 305 values.add(value); 306 } 307 } 308 } 309 } else if (childElems.length > 1) { 310 StringBuilder buf = new StringBuilder(100); 311 buf.append(MSG_INVALID_XMLA); 312 buf.append(": Wrong number of RestrictionList elements: "); 313 buf.append(childElems.length); 314 throw new XmlaException( 315 CLIENT_FAULT_FC, 316 HSB_BAD_RESTRICTION_LIST_CODE, 317 HSB_BAD_RESTRICTION_LIST_FAULT_FS, 318 Util.newError(buf.toString())); 319 } else { 320 } 321 this.restrictions = (Map) Collections.unmodifiableMap(restrictions); 322 } 323 324 private void initProperties(Element propertiesRoot) throws XmlaException { 325 Map<String, String> properties = new HashMap<String, String>(); 326 Element[] childElems = XmlaUtil.filterChildElements(propertiesRoot, 327 NS_XMLA, 328 "PropertyList"); 329 if (childElems.length == 1) { 330 NodeList nlst = childElems[0].getChildNodes(); 331 for (int i = 0, nlen = nlst.getLength(); i < nlen; i++) { 332 Node n = nlst.item(i); 333 if (n instanceof Element) { 334 Element e = (Element) n; 335 if (NS_XMLA.equals(e.getNamespaceURI())) { 336 337 String key = e.getLocalName(); 338 String value = XmlaUtil.textInElement(e); 339 340 if (LOGGER.isDebugEnabled()) { 341 StringBuilder buf = new StringBuilder(100); 342 buf.append("DefaultXmlaRequest.initProperties: "); 343 buf.append(" key=\""); 344 buf.append(key); 345 buf.append("\", value=\""); 346 buf.append(value); 347 buf.append("\""); 348 LOGGER.debug(buf.toString()); 349 } 350 351 properties.put(key, value); 352 } 353 } 354 } 355 } else if (childElems.length > 1) { 356 StringBuilder buf = new StringBuilder(100); 357 buf.append(MSG_INVALID_XMLA); 358 buf.append(": Wrong number of PropertyList elements: "); 359 buf.append(childElems.length); 360 throw new XmlaException( 361 CLIENT_FAULT_FC, 362 HSB_BAD_PROPERTIES_LIST_CODE, 363 HSB_BAD_PROPERTIES_LIST_FAULT_FS, 364 Util.newError(buf.toString())); 365 } else { 366 } 367 this.properties = Collections.unmodifiableMap(properties); 368 } 369 370 371 private void initCommand(Element commandRoot) throws XmlaException { 372 Element[] childElems = XmlaUtil.filterChildElements(commandRoot, 373 NS_XMLA, 374 "Statement"); 375 if (childElems.length != 1) { 376 StringBuilder buf = new StringBuilder(100); 377 buf.append(MSG_INVALID_XMLA); 378 buf.append(": Wrong number of Statement elements: "); 379 buf.append(childElems.length); 380 throw new XmlaException( 381 CLIENT_FAULT_FC, 382 HSB_BAD_STATEMENT_CODE, 383 HSB_BAD_STATEMENT_FAULT_FS, 384 Util.newError(buf.toString())); 385 } 386 statement = XmlaUtil.textInElement(childElems[0]).replaceAll("\\r", ""); 387 388 String upperStatement = statement.toUpperCase(); 389 int dtOffset = upperStatement.indexOf("DRILLTHROUGH"); 390 int mrOffset = upperStatement.indexOf("MAXROWS"); 391 int frOffset = upperStatement.indexOf("FIRSTROWSET"); 392 int slOffset = upperStatement.indexOf("SELECT"); 393 394 if (dtOffset == -1) { 395 drillthrough = false; 396 } else { 397 /* 398 * <drillthrough> := DRILLTHROUGH 399 * [<Max_Rows>] [<First_Rowset>] <MDX select> [<Return_Columns>] 400 * <Max_Rows> := MAXROWS <positive number> 401 * <First_Rowset> := FIRSTROWSET <positive number> 402 * <Return_Columns> := RETURN <member or attribute> 403 * [, <member or attribute>] 404 */ 405 if (dtOffset < slOffset) { 406 maxRows = firstRowset = -1; 407 try { 408 if (mrOffset > dtOffset && mrOffset < slOffset) { 409 maxRows = parseIntValue(statement.substring(mrOffset, slOffset)); 410 if (maxRows <= 0) { 411 StringBuilder buf = new StringBuilder(100); 412 buf.append(MSG_INVALID_MAXROWS); 413 buf.append(": "); 414 buf.append(maxRows); 415 throw new XmlaException( 416 CLIENT_FAULT_FC, 417 HSB_DRILLDOWN_BAD_MAXROWS_CODE, 418 HSB_DRILLDOWN_BAD_MAXROWS_FAULT_FS, 419 Util.newError(buf.toString())); 420 } 421 } 422 if (frOffset > dtOffset && frOffset > mrOffset && frOffset < slOffset) { 423 firstRowset = parseIntValue(statement.substring(frOffset, slOffset)); 424 if (firstRowset <= 0) { 425 StringBuilder buf = new StringBuilder(100); 426 buf.append(MSG_INVALID_FIRSTROWSET); 427 buf.append(": "); 428 buf.append(firstRowset); 429 throw new XmlaException( 430 CLIENT_FAULT_FC, 431 HSB_DRILLDOWN_BAD_FIRST_ROWSET_CODE, 432 HSB_DRILLDOWN_BAD_FIRST_ROWSET_FAULT_FS, 433 Util.newError(buf.toString())); 434 } 435 } 436 } catch (XmlaException xex) { 437 throw xex; 438 } catch (Exception e) { 439 throw new XmlaException( 440 CLIENT_FAULT_FC, 441 HSB_DRILLDOWN_ERROR_CODE, 442 HSB_DRILLDOWN_ERROR_FAULT_FS, 443 Util.newError(e, MSG_INVALID_DRILLTHROUGH)); 444 } 445 446 int configMaxRows = MondrianProperties.instance().MaxRows.get(); 447 if (configMaxRows > 0 && maxRows > configMaxRows) { 448 maxRows = configMaxRows; 449 } 450 451 StringBuilder dtStmtBuf = new StringBuilder(); 452 dtStmtBuf.append(statement.substring(0, dtOffset)); // formulas 453 dtStmtBuf.append(statement.substring(slOffset)); // select to end 454 statement = dtStmtBuf.toString(); 455 456 drillthrough = true; 457 } else { 458 throw new XmlaException( 459 CLIENT_FAULT_FC, 460 HSB_DRILLDOWN_ERROR_CODE, 461 HSB_DRILLDOWN_ERROR_FAULT_FS, 462 Util.newError(MSG_INVALID_DRILLTHROUGH)); 463 } 464 } 465 } 466 467 private int parseIntValue(String option) { 468 String[] opts = option.split("[ \t\n]"); 469 return Integer.parseInt(opts[1]); 470 } 471 472 } 473 474 // End DefaultXmlaRequest.java