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-2011 Pentaho 008// All Rights Reserved. 009*/ 010package mondrian.xmla.impl; 011 012import mondrian.xmla.*; 013 014import org.olap4j.impl.Olap4jUtil; 015 016import org.w3c.dom.*; 017import org.xml.sax.InputSource; 018import org.xml.sax.SAXException; 019 020import java.io.*; 021import java.nio.ByteBuffer; 022import java.nio.channels.*; 023import java.util.HashMap; 024import java.util.Map; 025import javax.servlet.ServletConfig; 026import javax.servlet.ServletException; 027import javax.servlet.http.HttpServletRequest; 028import javax.servlet.http.HttpServletResponse; 029import javax.xml.parsers.*; 030 031/** 032 * Default implementation of XML/A servlet. 033 * 034 * @author Gang Chen 035 */ 036public abstract class DefaultXmlaServlet extends XmlaServlet { 037 038 protected static final String nl = System.getProperty("line.separator"); 039 040 /** 041 * Servlet config parameter that determines whether the xmla servlet 042 * requires authenticated sessions. 043 */ 044 private static final String REQUIRE_AUTHENTICATED_SESSIONS = 045 "requireAuthenticatedSessions"; 046 047 private DocumentBuilderFactory domFactory = null; 048 049 private boolean requireAuthenticatedSessions = false; 050 051 /** 052 * Session properties, keyed by session ID. Currently just username and 053 * password. 054 */ 055 private final Map<String, SessionInfo> sessionInfos = 056 new HashMap<String, SessionInfo>(); 057 058 public void init(ServletConfig servletConfig) throws ServletException { 059 super.init(servletConfig); 060 this.domFactory = getDocumentBuilderFactory(); 061 this.requireAuthenticatedSessions = 062 Boolean.parseBoolean( 063 servletConfig.getInitParameter(REQUIRE_AUTHENTICATED_SESSIONS)); 064 } 065 066 protected static DocumentBuilderFactory getDocumentBuilderFactory() { 067 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 068 factory.setIgnoringComments(true); 069 factory.setIgnoringElementContentWhitespace(true); 070 factory.setNamespaceAware(true); 071 return factory; 072 } 073 074 protected void unmarshallSoapMessage( 075 HttpServletRequest request, 076 Element[] requestSoapParts) 077 throws XmlaException 078 { 079 try { 080 InputStream inputStream; 081 try { 082 inputStream = request.getInputStream(); 083 } catch (IllegalStateException ex) { 084 throw new XmlaException( 085 SERVER_FAULT_FC, 086 USM_REQUEST_STATE_CODE, 087 USM_REQUEST_STATE_FAULT_FS, 088 ex); 089 } catch (IOException ex) { 090 // This is either Client or Server 091 throw new XmlaException( 092 SERVER_FAULT_FC, 093 USM_REQUEST_INPUT_CODE, 094 USM_REQUEST_INPUT_FAULT_FS, 095 ex); 096 } 097 098 DocumentBuilder domBuilder; 099 try { 100 domBuilder = domFactory.newDocumentBuilder(); 101 } catch (ParserConfigurationException ex) { 102 throw new XmlaException( 103 SERVER_FAULT_FC, 104 USM_DOM_FACTORY_CODE, 105 USM_DOM_FACTORY_FAULT_FS, 106 ex); 107 } 108 109 Document soapDoc; 110 try { 111 soapDoc = domBuilder.parse(new InputSource(inputStream)); 112 } catch (IOException ex) { 113 // This is either Client or Server 114 throw new XmlaException( 115 SERVER_FAULT_FC, 116 USM_DOM_PARSE_IO_CODE, 117 USM_DOM_PARSE_IO_FAULT_FS, 118 ex); 119 } catch (SAXException ex) { 120 // Assume client passed bad xml 121 throw new XmlaException( 122 CLIENT_FAULT_FC, 123 USM_DOM_PARSE_CODE, 124 USM_DOM_PARSE_FAULT_FS, 125 ex); 126 } 127 128 /* Check SOAP message */ 129 Element envElem = soapDoc.getDocumentElement(); 130 131 if (LOGGER.isDebugEnabled()) { 132 logXmlaRequest(envElem); 133 } 134 135 if ("Envelope".equals(envElem.getLocalName())) { 136 if (!(NS_SOAP_ENV_1_1.equals(envElem.getNamespaceURI()))) { 137 throw new XmlaException( 138 CLIENT_FAULT_FC, 139 USM_DOM_PARSE_CODE, 140 USM_DOM_PARSE_FAULT_FS, 141 new SAXException( 142 "Invalid SOAP message: " 143 + "Envelope element not in SOAP namespace")); 144 } 145 } else { 146 throw new XmlaException( 147 CLIENT_FAULT_FC, 148 USM_DOM_PARSE_CODE, 149 USM_DOM_PARSE_FAULT_FS, 150 new SAXException( 151 "Invalid SOAP message: " 152 + "Top element not Envelope")); 153 } 154 155 Element[] childs = 156 XmlaUtil.filterChildElements( 157 envElem, NS_SOAP_ENV_1_1, "Header"); 158 if (childs.length > 1) { 159 throw new XmlaException( 160 CLIENT_FAULT_FC, 161 USM_DOM_PARSE_CODE, 162 USM_DOM_PARSE_FAULT_FS, 163 new SAXException( 164 "Invalid SOAP message: " 165 + "More than one Header elements")); 166 } 167 requestSoapParts[0] = childs.length == 1 ? childs[0] : null; 168 169 childs = 170 XmlaUtil.filterChildElements(envElem, NS_SOAP_ENV_1_1, "Body"); 171 if (childs.length != 1) { 172 throw new XmlaException( 173 CLIENT_FAULT_FC, 174 USM_DOM_PARSE_CODE, 175 USM_DOM_PARSE_FAULT_FS, 176 new SAXException( 177 "Invalid SOAP message: " 178 + "Does not have one Body element")); 179 } 180 requestSoapParts[1] = childs[0]; 181 } catch (XmlaException xex) { 182 throw xex; 183 } catch (Exception ex) { 184 throw new XmlaException( 185 SERVER_FAULT_FC, 186 USM_UNKNOWN_CODE, 187 USM_UNKNOWN_FAULT_FS, 188 ex); 189 } 190 } 191 192 protected void logXmlaRequest(Element envElem) { 193 final StringWriter writer = new StringWriter(); 194 writer.write("XML/A request content"); 195 writer.write(nl); 196 XmlaUtil.element2Text(envElem, writer); 197 LOGGER.debug(writer.toString()); 198 } 199 200 /** 201 * {@inheritDoc} 202 * 203 * <p>See if there is a "mustUnderstand" header element. 204 * If there is a BeginSession element, then generate a session id and 205 * add to context Map.</p> 206 * 207 * <p>Excel 2000 and Excel XP generate both a BeginSession, Session and 208 * EndSession mustUnderstand=1 209 * in the "urn:schemas-microsoft-com:xml-analysis" namespace 210 * Header elements and a NamespaceCompatibility mustUnderstand=0 211 * in the "http://schemas.microsoft.com/analysisservices/2003/xmla" 212 * namespace. Here we handle only the session Header elements. 213 * 214 * <p>We also handle the Security element.</p> 215 */ 216 protected void handleSoapHeader( 217 HttpServletResponse response, 218 Element[] requestSoapParts, 219 byte[][] responseSoapParts, 220 Map<String, Object> context) throws XmlaException 221 { 222 try { 223 Element hdrElem = requestSoapParts[0]; 224 if ((hdrElem == null) || (! hdrElem.hasChildNodes())) { 225 return; 226 } 227 228 String encoding = response.getCharacterEncoding(); 229 230 byte[] bytes = null; 231 232 NodeList nlst = hdrElem.getChildNodes(); 233 int nlen = nlst.getLength(); 234 boolean authenticatedSession = false; 235 boolean beginSession = false; 236 for (int i = 0; i < nlen; i++) { 237 Node n = nlst.item(i); 238 if (!(n instanceof Element)) { 239 continue; 240 } 241 Element e = (Element) n; 242 String localName = e.getLocalName(); 243 244 if (localName.equals(XMLA_SECURITY) 245 && NS_SOAP_SECEXT.equals(e.getNamespaceURI())) 246 { 247 // Example: 248 // 249 // <Security xmlns="http://schemas.xmlsoap.org/ws/2002/04/secext"> 250 // <UsernameToken> 251 // <Username>MICHELE</Username> 252 // <Password Type="PasswordText">ROSSI</Password> 253 // </UsernameToken> 254 // </Security> 255 // <BeginSession mustUnderstand="1" 256 // xmlns="urn:schemas-microsoft-com:xml-analysis" /> 257 NodeList childNodes = e.getChildNodes(); 258 Element userNameToken = (Element) childNodes.item(1); 259 NodeList userNamePassword = userNameToken.getChildNodes(); 260 Element username = (Element) userNamePassword.item(1); 261 Element password = (Element) userNamePassword.item(3); 262 String userNameStr = 263 username.getChildNodes().item(0).getNodeValue(); 264 context.put(CONTEXT_XMLA_USERNAME, userNameStr); 265 String passwordStr = ""; 266 267 if (password.getChildNodes().item(0) != null) { 268 passwordStr = 269 password.getChildNodes().item(0).getNodeValue(); 270 } 271 272 context.put(CONTEXT_XMLA_PASSWORD, passwordStr); 273 274 if ("".equals(passwordStr) || null == passwordStr) { 275 LOGGER.warn( 276 "Security header for user [" + userNameStr 277 + "] provided without password"); 278 } 279 authenticatedSession = true; 280 continue; 281 } 282 283 // Make sure Element has mustUnderstand=1 attribute. 284 Attr attr = e.getAttributeNode(SOAP_MUST_UNDERSTAND_ATTR); 285 boolean mustUnderstandValue = 286 attr != null 287 && attr.getValue() != null 288 && attr.getValue().equals("1"); 289 290 if (!mustUnderstandValue) { 291 continue; 292 } 293 294 // Is it an XMLA element 295 if (!NS_XMLA.equals(e.getNamespaceURI())) { 296 continue; 297 } 298 // So, an XMLA mustUnderstand-er 299 // Do we know what to do with it 300 // We understand: 301 // BeginSession 302 // Session 303 // EndSession 304 305 String sessionIdStr; 306 if (localName.equals(XMLA_BEGIN_SESSION)) { 307 sessionIdStr = generateSessionId(context); 308 309 context.put(CONTEXT_XMLA_SESSION_ID, sessionIdStr); 310 context.put( 311 CONTEXT_XMLA_SESSION_STATE, 312 CONTEXT_XMLA_SESSION_STATE_BEGIN); 313 314 } else if (localName.equals(XMLA_SESSION)) { 315 sessionIdStr = getSessionIdFromRequest(e, context); 316 317 SessionInfo sessionInfo = getSessionInfo(sessionIdStr); 318 319 if (sessionInfo != null) { 320 context.put(CONTEXT_XMLA_USERNAME, sessionInfo.user); 321 context.put( 322 CONTEXT_XMLA_PASSWORD, 323 sessionInfo.password); 324 } 325 326 context.put(CONTEXT_XMLA_SESSION_ID, sessionIdStr); 327 context.put( 328 CONTEXT_XMLA_SESSION_STATE, 329 CONTEXT_XMLA_SESSION_STATE_WITHIN); 330 331 } else if (localName.equals(XMLA_END_SESSION)) { 332 sessionIdStr = getSessionIdFromRequest(e, context); 333 context.put( 334 CONTEXT_XMLA_SESSION_STATE, 335 CONTEXT_XMLA_SESSION_STATE_END); 336 337 } else { 338 // error 339 String msg = 340 "Invalid XML/A message: Unknown " 341 + "\"mustUnderstand\" XMLA Header element \"" 342 + localName 343 + "\""; 344 throw new XmlaException( 345 MUST_UNDERSTAND_FAULT_FC, 346 HSH_MUST_UNDERSTAND_CODE, 347 HSH_MUST_UNDERSTAND_FAULT_FS, 348 new RuntimeException(msg)); 349 } 350 351 StringBuilder buf = new StringBuilder(100); 352 buf.append("<Session "); 353 buf.append(XMLA_SESSION_ID); 354 buf.append("=\""); 355 buf.append(sessionIdStr); 356 buf.append("\" "); 357 buf.append("xmlns=\""); 358 buf.append(NS_XMLA); 359 buf.append("\" />"); 360 bytes = buf.toString().getBytes(encoding); 361 362 if (authenticatedSession) { 363 String username = 364 (String) context.get(CONTEXT_XMLA_USERNAME); 365 String password = 366 (String) context.get(CONTEXT_XMLA_PASSWORD); 367 String sessionId = 368 (String) context.get(CONTEXT_XMLA_SESSION_ID); 369 370 LOGGER.debug( 371 "New authenticated session; storing credentials [" 372 + username + "/********] for session id [" 373 + sessionId + "]"); 374 375 saveSessionInfo( 376 username, 377 password, 378 sessionId); 379 } else { 380 if (beginSession && requireAuthenticatedSessions) { 381 throw new XmlaException( 382 XmlaConstants.CLIENT_FAULT_FC, 383 XmlaConstants.CHH_AUTHORIZATION_CODE, 384 XmlaConstants.CHH_AUTHORIZATION_FAULT_FS, 385 new Exception("Session Credentials NOT PROVIDED")); 386 } 387 } 388 } 389 responseSoapParts[0] = bytes; 390 } catch (XmlaException xex) { 391 throw xex; 392 } catch (Exception ex) { 393 throw new XmlaException( 394 SERVER_FAULT_FC, 395 HSH_UNKNOWN_CODE, 396 HSH_UNKNOWN_FAULT_FS, 397 ex); 398 } 399 } 400 401 402 protected String generateSessionId(Map<String, Object> context) { 403 for (XmlaRequestCallback callback : getCallbacks()) { 404 final String sessionId = callback.generateSessionId(context); 405 if (sessionId != null) { 406 return sessionId; 407 } 408 } 409 410 411 // Generate a pseudo-random new session ID. 412 return Long.toString( 413 17L * System.nanoTime() 414 + 3L * System.currentTimeMillis(), 35); 415 } 416 417 418 private static String getSessionIdFromRequest( 419 Element e, 420 Map<String, Object> context) 421 throws Exception 422 { 423 // extract the SessionId attrs value and put into context 424 Attr attr = e.getAttributeNode(XMLA_SESSION_ID); 425 if (attr == null) { 426 throw new SAXException( 427 "Invalid XML/A message: " 428 + XMLA_SESSION 429 + " Header element with no " 430 + XMLA_SESSION_ID 431 + " attribute"); 432 } 433 434 String sessionId = attr.getValue(); 435 if (sessionId == null) { 436 throw new SAXException( 437 "Invalid XML/A message: " 438 + XMLA_SESSION 439 + " Header element with " 440 + XMLA_SESSION_ID 441 + " attribute but no attribute value"); 442 } 443 return sessionId; 444 } 445 446 protected void handleSoapBody( 447 HttpServletResponse response, 448 Element[] requestSoapParts, 449 byte[][] responseSoapParts, 450 Map<String, Object> context) 451 throws XmlaException 452 { 453 try { 454 String encoding = response.getCharacterEncoding(); 455 Element hdrElem = requestSoapParts[0]; // not used 456 Element bodyElem = requestSoapParts[1]; 457 Element[] dreqs = 458 XmlaUtil.filterChildElements(bodyElem, NS_XMLA, "Discover"); 459 Element[] ereqs = 460 XmlaUtil.filterChildElements(bodyElem, NS_XMLA, "Execute"); 461 if (dreqs.length + ereqs.length != 1) { 462 throw new XmlaException( 463 CLIENT_FAULT_FC, 464 HSB_BAD_SOAP_BODY_CODE, 465 HSB_BAD_SOAP_BODY_FAULT_FS, 466 new RuntimeException( 467 "Invalid XML/A message: Body has " 468 + dreqs.length + " Discover Requests and " 469 + ereqs.length + " Execute Requests")); 470 } 471 472 Element xmlaReqElem = (dreqs.length == 0 ? ereqs[0] : dreqs[0]); 473 474 ByteArrayOutputStream osBuf = new ByteArrayOutputStream(); 475 476 // use context variable 'role_name' as this request's XML/A role 477 String roleName = (String) context.get(CONTEXT_ROLE_NAME); 478 479 String username = (String) context.get(CONTEXT_XMLA_USERNAME); 480 String password = (String) context.get(CONTEXT_XMLA_PASSWORD); 481 String sessionId = (String) context.get(CONTEXT_XMLA_SESSION_ID); 482 XmlaRequest xmlaReq = 483 new DefaultXmlaRequest( 484 xmlaReqElem, roleName, username, password, sessionId); 485 486 // "ResponseMimeType" may be in the context if the "Accept" HTTP 487 // header was specified. But override if the SOAP request has the 488 // "ResponseMimeType" property. 489 Enumeration.ResponseMimeType responseMimeType = 490 Enumeration.ResponseMimeType.SOAP; 491 final String responseMimeTypeName = 492 xmlaReq.getProperties().get("ResponseMimeType"); 493 if (responseMimeTypeName != null) { 494 responseMimeType = 495 Enumeration.ResponseMimeType.MAP.get( 496 responseMimeTypeName); 497 if (responseMimeType != null) { 498 context.put(CONTEXT_MIME_TYPE, responseMimeType); 499 } 500 } 501 502 XmlaResponse xmlaRes = 503 new DefaultXmlaResponse(osBuf, encoding, responseMimeType); 504 505 try { 506 getXmlaHandler().process(xmlaReq, xmlaRes); 507 } catch (XmlaException ex) { 508 throw ex; 509 } catch (Exception ex) { 510 throw new XmlaException( 511 SERVER_FAULT_FC, 512 HSB_PROCESS_CODE, 513 HSB_PROCESS_FAULT_FS, 514 ex); 515 } 516 517 responseSoapParts[1] = osBuf.toByteArray(); 518 } catch (XmlaException xex) { 519 throw xex; 520 } catch (Exception ex) { 521 throw new XmlaException( 522 SERVER_FAULT_FC, 523 HSB_UNKNOWN_CODE, 524 HSB_UNKNOWN_FAULT_FS, 525 ex); 526 } 527 } 528 529 protected void marshallSoapMessage( 530 HttpServletResponse response, 531 byte[][] responseSoapParts, 532 Enumeration.ResponseMimeType responseMimeType) 533 throws XmlaException 534 { 535 try { 536 // If CharacterEncoding was set in web.xml, use this value 537 String encoding = 538 (charEncoding != null) 539 ? charEncoding 540 : response.getCharacterEncoding(); 541 542 /* 543 * Since we just reset response, encoding and content-type were 544 * reset too 545 */ 546 if (charEncoding != null) { 547 response.setCharacterEncoding(charEncoding); 548 } 549 switch (responseMimeType) { 550 case JSON: 551 response.setContentType("application/json"); 552 break; 553 case SOAP: 554 default: 555 response.setContentType("text/xml"); 556 break; 557 } 558 559 /* 560 * The setCharacterEncoding, setContentType, or setLocale method 561 * must be called BEFORE getWriter or getOutputStream and before 562 * committing the response for the character encoding to be used. 563 * 564 * @see javax.servlet.ServletResponse 565 */ 566 OutputStream outputStream = response.getOutputStream(); 567 568 569 byte[] soapHeader = responseSoapParts[0]; 570 byte[] soapBody = responseSoapParts[1]; 571 572 Object[] byteChunks = null; 573 574 try { 575 switch (responseMimeType) { 576 case JSON: 577 byteChunks = new Object[] { 578 soapBody, 579 }; 580 break; 581 582 case SOAP: 583 default: 584 String s0 = 585 "<?xml version=\"1.0\" encoding=\"" + encoding 586 + "\"?>\n<" + SOAP_PREFIX + ":Envelope xmlns:" 587 + SOAP_PREFIX + "=\"" + NS_SOAP_ENV_1_1 + "\" " 588 + SOAP_PREFIX + ":encodingStyle=\"" 589 + NS_SOAP_ENC_1_1 + "\" >" + "\n<" + SOAP_PREFIX 590 + ":Header>\n"; 591 String s2 = 592 "</" + SOAP_PREFIX + ":Header>\n<" + SOAP_PREFIX 593 + ":Body>\n"; 594 String s4 = 595 "\n</" + SOAP_PREFIX + ":Body>\n</" + SOAP_PREFIX 596 + ":Envelope>\n"; 597 598 byteChunks = new Object[] { 599 s0.getBytes(encoding), 600 soapHeader, 601 s2.getBytes(encoding), 602 soapBody, 603 s4.getBytes(encoding), 604 }; 605 break; 606 } 607 } catch (UnsupportedEncodingException uee) { 608 LOGGER.warn( 609 "This should be handled at begin of processing request", 610 uee); 611 } 612 613 if (LOGGER.isDebugEnabled()) { 614 StringBuilder buf = new StringBuilder(100); 615 buf.append("XML/A response content").append(nl); 616 try { 617 for (Object byteChunk : byteChunks) { 618 byte[] chunk = (byte[]) byteChunk; 619 if (chunk != null && chunk.length > 0) { 620 buf.append(new String(chunk, encoding)); 621 } 622 } 623 } catch (UnsupportedEncodingException uee) { 624 LOGGER.warn( 625 "This should be handled at begin of processing request", 626 uee); 627 } 628 LOGGER.debug(buf.toString()); 629 } 630 631 if (LOGGER.isDebugEnabled()) { 632 StringBuilder buf = new StringBuilder(); 633 buf.append("XML/A response content").append(nl); 634 } 635 try { 636 int bufferSize = 4096; 637 ByteBuffer buffer = ByteBuffer.allocate(bufferSize); 638 WritableByteChannel wch = Channels.newChannel(outputStream); 639 ReadableByteChannel rch; 640 for (Object byteChunk : byteChunks) { 641 if (byteChunk == null || ((byte[]) byteChunk).length == 0) { 642 continue; 643 } 644 rch = Channels.newChannel( 645 new ByteArrayInputStream((byte[]) byteChunk)); 646 647 int readSize; 648 do { 649 buffer.clear(); 650 readSize = rch.read(buffer); 651 buffer.flip(); 652 653 int writeSize = 0; 654 while ((writeSize += wch.write(buffer)) < readSize) { 655 } 656 } while (readSize == bufferSize); 657 rch.close(); 658 } 659 outputStream.flush(); 660 } catch (IOException ioe) { 661 LOGGER.error( 662 "Damn exception when transferring bytes over sockets", 663 ioe); 664 } 665 } catch (XmlaException xex) { 666 throw xex; 667 } catch (Exception ex) { 668 throw new XmlaException( 669 SERVER_FAULT_FC, 670 MSM_UNKNOWN_CODE, 671 MSM_UNKNOWN_FAULT_FS, 672 ex); 673 } 674 } 675 676 /** 677 * This produces a SOAP 1.1 version Fault element - not a 1.2 version. 678 * 679 */ 680 protected void handleFault( 681 HttpServletResponse response, 682 byte[][] responseSoapParts, 683 Phase phase, 684 Throwable t) 685 { 686 // Regardless of whats been put into the response so far, clear 687 // it out. 688 response.reset(); 689 690 // NOTE: if you can think of better/other status codes to use 691 // for the various phases, please make changes. 692 // I think that XMLA faults always returns OK. 693 switch (phase) { 694 case VALIDATE_HTTP_HEAD: 695 response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); 696 break; 697 case INITIAL_PARSE: 698 case CALLBACK_PRE_ACTION: 699 case PROCESS_HEADER: 700 case PROCESS_BODY: 701 case CALLBACK_POST_ACTION: 702 case SEND_RESPONSE: 703 response.setStatus(HttpServletResponse.SC_OK); 704 break; 705 } 706 707 String code; 708 String faultCode; 709 String faultString; 710 String detail; 711 if (t instanceof XmlaException) { 712 XmlaException xex = (XmlaException) t; 713 code = xex.getCode(); 714 faultString = xex.getFaultString(); 715 faultCode = XmlaException.formatFaultCode(xex); 716 detail = XmlaException.formatDetail(xex.getDetail()); 717 718 } else { 719 // some unexpected Throwable 720 t = XmlaException.getRootCause(t); 721 code = UNKNOWN_ERROR_CODE; 722 faultString = UNKNOWN_ERROR_FAULT_FS; 723 faultCode = XmlaException.formatFaultCode( 724 SERVER_FAULT_FC, code); 725 detail = XmlaException.formatDetail(t.getMessage()); 726 } 727 728 String encoding = response.getCharacterEncoding(); 729 730 ByteArrayOutputStream osBuf = new ByteArrayOutputStream(); 731 try { 732 SaxWriter writer = new DefaultSaxWriter(osBuf, encoding); 733 writer.startDocument(); 734 writer.startElement(SOAP_PREFIX + ":Fault"); 735 736 // The faultcode element is intended for use by software to provide 737 // an algorithmic mechanism for identifying the fault. The faultcode 738 // MUST be present in a SOAP Fault element and the faultcode value 739 // MUST be a qualified name 740 writer.startElement("faultcode"); 741 writer.characters(faultCode); 742 writer.endElement(); 743 744 // The faultstring element is intended to provide a human readable 745 // explanation of the fault and is not intended for algorithmic 746 // processing. 747 writer.startElement("faultstring"); 748 writer.characters(faultString); 749 writer.endElement(); 750 751 // The faultactor element is intended to provide information about 752 // who caused the fault to happen within the message path 753 writer.startElement("faultactor"); 754 writer.characters(FAULT_ACTOR); 755 writer.endElement(); 756 757 // The detail element is intended for carrying application specific 758 // error information related to the Body element. It MUST be present 759 // if the contents of the Body element could not be successfully 760 // processed. It MUST NOT be used to carry information about error 761 // information belonging to header entries. Detailed error 762 // information belonging to header entries MUST be carried within 763 // header entries. 764 if (phase != Phase.PROCESS_HEADER) { 765 writer.startElement("detail"); 766 writer.startElement( 767 FAULT_NS_PREFIX + ":error", 768 "xmlns:" + FAULT_NS_PREFIX, MONDRIAN_NAMESPACE); 769 writer.startElement("code"); 770 writer.characters(code); 771 writer.endElement(); // code 772 writer.startElement("desc"); 773 writer.characters(detail); 774 writer.endElement(); // desc 775 writer.endElement(); // error 776 writer.endElement(); // detail 777 } 778 779 writer.endElement(); // </Fault> 780 writer.endDocument(); 781 } catch (UnsupportedEncodingException uee) { 782 LOGGER.warn( 783 "This should be handled at begin of processing request", 784 uee); 785 } catch (Exception e) { 786 LOGGER.error( 787 "Unexcepted runimt exception when handing SOAP fault :("); 788 } 789 790 responseSoapParts[1] = osBuf.toByteArray(); 791 } 792 793 private SessionInfo getSessionInfo(String sessionId) { 794 if (sessionId == null) { 795 return null; 796 } 797 798 SessionInfo sessionInfo = null; 799 800 synchronized (sessionInfos) { 801 sessionInfo = sessionInfos.get(sessionId); 802 } 803 804 if (sessionInfo == null) { 805 LOGGER.error( 806 "No login credentials for found for session ["+ 807 sessionId + "]"); 808 } else { 809 LOGGER.debug( 810 "Found credentials for session id [" 811 + sessionId 812 + "], username=[" + sessionInfo.user 813 + "] in servlet cache"); 814 } 815 return sessionInfo; 816 } 817 818 private SessionInfo saveSessionInfo( 819 String username, 820 String password, 821 String sessionId) 822 { 823 synchronized (sessionInfos) { 824 SessionInfo sessionInfo = sessionInfos.get(sessionId); 825 if (sessionInfo != null 826 && Olap4jUtil.equal(sessionInfo.user, username)) 827 { 828 // Overwrite the password, but only if it is non-empty. 829 // (Sometimes Simba sends the credentials object again 830 // but without a password.) 831 if (password != null && password.length() > 0) { 832 sessionInfo = 833 new SessionInfo(username, password); 834 sessionInfos.put(sessionId, sessionInfo); 835 } 836 } else { 837 // A credentials object was stored against the provided session 838 // ID but the username didn't match, so create a new holder. 839 sessionInfo = new SessionInfo(username, password); 840 sessionInfos.put(sessionId, sessionInfo); 841 } 842 return sessionInfo; 843 } 844 } 845 /** 846 * Holds authentication credentials of a XMLA session. 847 */ 848 private static class SessionInfo { 849 final String user; 850 final String password; 851 852 public SessionInfo(String user, String password) 853 { 854 this.user = user; 855 this.password = password; 856 } 857 } 858} 859// End DefaultXmlaServlet.java