1 10 package mondrian.xmla.impl; 11 12 import java.io.ByteArrayInputStream ; 13 import java.io.ByteArrayOutputStream ; 14 import java.io.IOException ; 15 import java.io.InputStream ; 16 import java.io.OutputStream ; 17 import java.io.UnsupportedEncodingException ; 18 import java.nio.ByteBuffer ; 19 import java.nio.channels.Channels ; 20 import java.nio.channels.ReadableByteChannel ; 21 import java.nio.channels.WritableByteChannel ; 22 import java.util.Map ; 23 import java.util.List ; 24 25 import javax.servlet.ServletConfig ; 26 import javax.servlet.ServletException ; 27 import javax.servlet.http.HttpServletRequest ; 28 import javax.servlet.http.HttpServletResponse ; 29 import javax.xml.parsers.DocumentBuilder ; 30 import javax.xml.parsers.DocumentBuilderFactory ; 31 import javax.xml.parsers.ParserConfigurationException ; 32 33 import mondrian.xmla.SaxWriter; 34 import mondrian.xmla.XmlaRequest; 35 import mondrian.xmla.XmlaResponse; 36 import mondrian.xmla.XmlaServlet; 37 import mondrian.xmla.XmlaUtil; 38 import mondrian.olap.Util; 39 import mondrian.xmla.XmlaRequestCallback; 40 import mondrian.xmla.XmlaException; 41 42 import org.apache.log4j.Logger; 43 import org.w3c.dom.Attr ; 44 import org.w3c.dom.Document ; 45 import org.w3c.dom.Node ; 46 import org.w3c.dom.NodeList ; 47 import org.w3c.dom.Element ; 48 import org.xml.sax.InputSource ; 49 import org.xml.sax.SAXException ; 50 51 56 public class DefaultXmlaServlet extends XmlaServlet { 57 58 private static final Logger LOGGER = Logger.getLogger(DefaultXmlaServlet.class); 59 protected static final String nl = Util.nl; 60 61 private DocumentBuilderFactory domFactory = null; 62 63 public void init(ServletConfig servletConfig) throws ServletException { 64 super.init(servletConfig); 65 domFactory = getDocumentBuilderFactory(); 66 } 67 68 protected DocumentBuilderFactory getDocumentBuilderFactory() { 69 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 70 factory.setIgnoringComments(true); 71 factory.setIgnoringElementContentWhitespace(true); 72 factory.setNamespaceAware(true); 73 return factory; 74 } 75 76 protected void unmarshallSoapMessage( 77 HttpServletRequest request, 78 Element [] requestSoapParts) 79 throws XmlaException { 80 81 try { 82 InputStream inputStream; 83 try { 84 inputStream = request.getInputStream(); 85 } catch (IllegalStateException ex) { 86 throw new XmlaException( 87 SERVER_FAULT_FC, 88 USM_REQUEST_STATE_CODE, 89 USM_REQUEST_STATE_FAULT_FS, 90 ex); 91 } catch (IOException ex) { 92 throw new XmlaException( 94 SERVER_FAULT_FC, 95 USM_REQUEST_INPUT_CODE, 96 USM_REQUEST_INPUT_FAULT_FS, 97 ex); 98 } 99 100 DocumentBuilder domBuilder; 101 try { 102 domBuilder = domFactory.newDocumentBuilder(); 103 } catch (ParserConfigurationException ex) { 104 throw new XmlaException( 105 SERVER_FAULT_FC, 106 USM_DOM_FACTORY_CODE, 107 USM_DOM_FACTORY_FAULT_FS, 108 ex); 109 } 110 111 Document soapDoc; 112 try { 113 soapDoc = domBuilder.parse(new InputSource (inputStream)); 114 } catch (IOException ex) { 115 throw new XmlaException( 117 SERVER_FAULT_FC, 118 USM_DOM_PARSE_IO_CODE, 119 USM_DOM_PARSE_IO_FAULT_FS, 120 ex); 121 } catch (SAXException ex) { 122 throw new XmlaException( 124 CLIENT_FAULT_FC, 125 USM_DOM_PARSE_CODE, 126 USM_DOM_PARSE_FAULT_FS, 127 ex); 128 } 129 130 131 Element envElem = soapDoc.getDocumentElement(); 132 133 if (LOGGER.isDebugEnabled()) { 134 StringBuilder buf = new StringBuilder (100); 135 buf.append("XML/A request content").append(nl); 136 buf.append(XmlaUtil.element2Text(envElem)); 137 LOGGER.debug(buf.toString()); 138 } 139 140 if ("Envelope".equals(envElem.getLocalName())) { 141 if (!(NS_SOAP_ENV_1_1.equals(envElem.getNamespaceURI()))) { 142 String msg = "Invalid SOAP message: " + 143 "Envelope element not in SOAP namespace"; 144 throw new XmlaException( 145 CLIENT_FAULT_FC, 146 USM_DOM_PARSE_CODE, 147 USM_DOM_PARSE_FAULT_FS, 148 new SAXException (msg)); 149 } 150 } else { 151 String msg = "Invalid SOAP message: " + 152 "Top element not Envelope"; 153 throw new XmlaException( 154 CLIENT_FAULT_FC, 155 USM_DOM_PARSE_CODE, 156 USM_DOM_PARSE_FAULT_FS, 157 new SAXException (msg)); 158 } 159 160 Element [] childs = 161 XmlaUtil.filterChildElements(envElem, NS_SOAP_ENV_1_1, "Header"); 162 if (childs.length > 1) { 163 String msg = "Invalid SOAP message: " + 164 "More than one Header elements"; 165 throw new XmlaException( 166 CLIENT_FAULT_FC, 167 USM_DOM_PARSE_CODE, 168 USM_DOM_PARSE_FAULT_FS, 169 new SAXException (msg)); 170 } 171 requestSoapParts[0] = childs.length == 1 ? childs[0] : null; 172 173 childs = XmlaUtil.filterChildElements(envElem, NS_SOAP_ENV_1_1, "Body"); 174 if (childs.length != 1) { 175 String msg = "Invalid SOAP message: " + 176 "Does not have one Body element"; 177 throw new XmlaException( 178 CLIENT_FAULT_FC, 179 USM_DOM_PARSE_CODE, 180 USM_DOM_PARSE_FAULT_FS, 181 new SAXException (msg)); 182 } 183 requestSoapParts[1] = childs[0]; 184 185 } catch (XmlaException xex) { 186 throw xex; 187 } catch (Exception ex) { 188 throw new XmlaException( 189 SERVER_FAULT_FC, 190 USM_UNKNOWN_CODE, 191 USM_UNKNOWN_FAULT_FS, 192 ex); 193 } 194 } 195 196 209 protected void handleSoapHeader( 210 HttpServletResponse response, 211 Element [] requestSoapParts, 212 byte[][] responseSoapParts, 213 Map <String , String > context) throws XmlaException { 214 215 try { 216 Element hdrElem = requestSoapParts[0]; 217 if ((hdrElem == null) || (! hdrElem.hasChildNodes())) { 218 return; 219 } 220 String encoding = response.getCharacterEncoding(); 221 222 byte[] bytes = null; 223 224 NodeList nlst = hdrElem.getChildNodes(); 225 int nlen = nlst.getLength(); 226 for (int i = 0; i < nlen; i++) { 227 Node n = nlst.item(i); 228 if (n instanceof Element ) { 229 Element e = (Element ) n; 230 231 Attr attr = e.getAttributeNode(SOAP_MUST_UNDERSTAND_ATTR); 233 if (attr == null) { 234 continue; 235 } 236 String mustUnderstandValue = attr.getValue(); 238 if ((mustUnderstandValue == null) || 239 (! mustUnderstandValue.equals("1"))) { 240 continue; 241 } 242 243 245 if (! NS_XMLA.equals(e.getNamespaceURI())) { 247 continue; 248 } 249 256 String sessionIdStr; 257 String localName = e.getLocalName(); 258 if (localName.equals(XMLA_BEGIN_SESSION)) { 259 261 sessionIdStr = generateSessionId(context); 262 263 context.put(CONTEXT_XMLA_SESSION_ID, sessionIdStr); 264 context.put(CONTEXT_XMLA_SESSION_STATE, 265 CONTEXT_XMLA_SESSION_STATE_BEGIN); 266 267 } else if (localName.equals(XMLA_SESSION)) { 268 sessionIdStr = getSessionId(e, context); 270 271 context.put(CONTEXT_XMLA_SESSION_ID, sessionIdStr); 272 context.put(CONTEXT_XMLA_SESSION_STATE, 273 CONTEXT_XMLA_SESSION_STATE_WITHIN); 274 275 } else if (localName.equals(XMLA_END_SESSION)) { 276 sessionIdStr = getSessionId(e, context); 278 279 context.put(CONTEXT_XMLA_SESSION_ID, sessionIdStr); 280 context.put(CONTEXT_XMLA_SESSION_STATE, 281 CONTEXT_XMLA_SESSION_STATE_END); 282 283 } else { 284 String msg = "Invalid XML/A message: " + 286 " Unknown \"mustUnderstand\" XMLA Header element \"" + 287 localName + 288 "\""; 289 throw new XmlaException( 290 MUST_UNDERSTAND_FAULT_FC, 291 HSH_MUST_UNDERSTAND_CODE, 292 HSH_MUST_UNDERSTAND_FAULT_FS, 293 new RuntimeException (msg) 294 ); 295 } 296 297 StringBuilder buf = new StringBuilder (100); 298 buf.append("<Session "); 299 buf.append(XMLA_SESSION_ID); 300 buf.append("=\""); 301 buf.append(sessionIdStr); 302 buf.append("\" "); 303 buf.append("xmlns=\""); 304 buf.append(NS_XMLA); 305 buf.append("\" />"); 306 bytes = buf.toString().getBytes(encoding); 307 308 } 309 } 310 responseSoapParts[0] = bytes; 311 312 } catch (XmlaException xex) { 313 throw xex; 314 } catch (Exception ex) { 315 throw new XmlaException( 316 SERVER_FAULT_FC, 317 HSH_UNKNOWN_CODE, 318 HSH_UNKNOWN_FAULT_FS, 319 ex); 320 } 321 } 322 323 protected String generateSessionId(Map <String , String > context) { 324 List <XmlaRequestCallback> callbacks = getCallbacks(); 325 if (callbacks.size() > 0) { 326 XmlaRequestCallback callback = callbacks.get(0); 328 return callback.generateSessionId(context); 329 } else { 330 return ""; 334 } 335 } 336 337 protected String getSessionId(Element e, Map <String , String > context) 338 throws Exception { 339 Attr attr = e.getAttributeNode(XMLA_SESSION_ID); 341 if (attr == null) { 342 String msg = "Invalid XML/A message: " + 343 XMLA_SESSION + 344 " Header element with no " + 345 XMLA_SESSION_ID + 346 " attribute"; 347 throw new SAXException (msg); 348 } 349 String value = attr.getValue(); 350 if (value == null) { 351 String msg = "Invalid XML/A message: " + 352 XMLA_SESSION + 353 " Header element with " + 354 XMLA_SESSION_ID + 355 " attribute but no attribute value"; 356 throw new SAXException (msg); 357 } 358 return value; 359 } 360 361 protected void handleSoapBody( 362 HttpServletResponse response, 363 Element [] requestSoapParts, 364 byte[][] responseSoapParts, 365 Map <String , String > context) 366 throws XmlaException 367 { 368 try { 369 String encoding = response.getCharacterEncoding(); 370 Element hdrElem = requestSoapParts[0]; 371 Element bodyElem = requestSoapParts[1]; 372 Element [] dreqs = XmlaUtil.filterChildElements(bodyElem, NS_XMLA, "Discover"); 373 Element [] ereqs = XmlaUtil.filterChildElements(bodyElem, NS_XMLA, "Execute"); 374 if (dreqs.length + ereqs.length != 1) { 375 String msg = "Invalid XML/A message: " + 376 " Body has " + 377 dreqs.length + 378 " Discover Requests and " + 379 ereqs.length + 380 " Execute Requests"; 381 throw new XmlaException( 382 CLIENT_FAULT_FC, 383 HSB_BAD_SOAP_BODY_CODE, 384 HSB_BAD_SOAP_BODY_FAULT_FS, 385 new RuntimeException (msg) 386 ); 387 } 388 389 Element xmlaReqElem = (dreqs.length == 0 ? ereqs[0] : dreqs[0]); 390 391 ByteArrayOutputStream osBuf = new ByteArrayOutputStream (); 392 393 XmlaRequest xmlaReq = new DefaultXmlaRequest( 395 xmlaReqElem, 396 context.get(CONTEXT_ROLE)); 397 XmlaResponse xmlaRes = new DefaultXmlaResponse(osBuf, encoding); 398 399 try { 400 getXmlaHandler().process(xmlaReq, xmlaRes); 401 } catch (XmlaException ex) { 402 throw ex; 403 } catch (Exception ex) { 404 throw new XmlaException( 405 SERVER_FAULT_FC, 406 HSB_PROCESS_CODE, 407 HSB_PROCESS_FAULT_FS, 408 ex); 409 } 410 411 responseSoapParts[1] = osBuf.toByteArray(); 412 413 } catch (XmlaException xex) { 414 throw xex; 415 } catch (Exception ex) { 416 throw new XmlaException( 417 SERVER_FAULT_FC, 418 HSB_UNKNOWN_CODE, 419 HSB_UNKNOWN_FAULT_FS, 420 ex); 421 } 422 } 423 424 protected void marshallSoapMessage( 425 HttpServletResponse response, 426 byte[][] responseSoapParts) 427 throws XmlaException { 428 429 try { 430 String encoding = (charEncoding != null) 432 ? charEncoding : response.getCharacterEncoding(); 433 434 438 if (charEncoding != null) { 439 response.setCharacterEncoding(charEncoding); 440 } 441 response.setContentType("text/xml"); 442 443 449 OutputStream outputStream = response.getOutputStream(); 450 451 452 byte[] soapHeader = responseSoapParts[0]; 453 byte[] soapBody = responseSoapParts[1]; 454 455 Object [] byteChunks = new Object [5]; 456 457 try { 458 StringBuilder buf = new StringBuilder (500); 459 buf.append("<?xml version=\"1.0\" encoding=\""); 460 buf.append(encoding); 461 buf.append("\"?>"); 462 buf.append(nl); 463 464 buf.append("<"); 465 buf.append(SOAP_PREFIX); 466 buf.append(":Envelope xmlns:"); 467 buf.append(SOAP_PREFIX); 468 buf.append("=\""); 469 buf.append(NS_SOAP_ENV_1_1); 470 buf.append("\" "); 471 buf.append(SOAP_PREFIX); 472 buf.append(":encodingStyle=\""); 473 buf.append(NS_SOAP_ENC_1_1); 474 buf.append("\" >"); 475 buf.append(nl); 476 buf.append("<"); 477 buf.append(SOAP_PREFIX); 478 buf.append(":Header>"); 479 buf.append(nl); 480 byteChunks[0] = buf.toString().getBytes(encoding); 481 482 byteChunks[1] = soapHeader; 483 484 buf.setLength(0); 485 buf.append("</"); 486 buf.append(SOAP_PREFIX); 487 buf.append(":Header>"); 488 buf.append(nl); 489 buf.append("<"); 490 buf.append(SOAP_PREFIX); 491 buf.append(":Body>"); 492 buf.append(nl); 493 494 byteChunks[2] = buf.toString().getBytes(encoding); 495 496 byteChunks[3] = soapBody; 497 498 buf.setLength(0); 499 buf.append(nl); 500 buf.append("</"); 501 buf.append(SOAP_PREFIX); 502 buf.append(":Body>"); 503 buf.append(nl); 504 buf.append("</"); 505 buf.append(SOAP_PREFIX); 506 buf.append(":Envelope>"); 507 buf.append(nl); 508 509 byteChunks[4] = buf.toString().getBytes(encoding); 510 511 } catch (UnsupportedEncodingException uee) { 512 LOGGER.warn("This should be handled at begin of processing request", uee); 513 } 514 515 if (LOGGER.isDebugEnabled()) { 516 StringBuilder buf = new StringBuilder (100); 517 buf.append("XML/A response content").append(nl); 518 try { 519 for (Object byteChunk : byteChunks) { 520 byte[] chunk = (byte[]) byteChunk; 521 if (chunk != null && chunk.length > 0) { 522 buf.append(new String (chunk, encoding)); 523 } 524 } 525 } catch (UnsupportedEncodingException uee) { 526 LOGGER.warn("This should be handled at begin of processing request", uee); 527 } 528 LOGGER.debug(buf.toString()); 529 } 530 531 if (LOGGER.isDebugEnabled()) { 532 StringBuilder buf = new StringBuilder (); 533 buf.append("XML/A response content").append(nl); 534 } 535 try { 536 int bufferSize = 4096; 537 ByteBuffer buffer = ByteBuffer.allocate(bufferSize); 538 WritableByteChannel wch = Channels.newChannel(outputStream); 539 ReadableByteChannel rch; 540 for (Object byteChunk : byteChunks) { 541 if (byteChunk == null || ((byte[]) byteChunk).length == 0) { 542 continue; 543 } 544 rch = Channels 545 .newChannel(new ByteArrayInputStream ((byte[]) byteChunk)); 546 547 int readSize; 548 do { 549 buffer.clear(); 550 readSize = rch.read(buffer); 551 buffer.flip(); 552 553 int writeSize = 0; 554 while ((writeSize += wch.write(buffer)) < readSize) { 555 ; 556 } 557 } while (readSize == bufferSize); 558 rch.close(); 559 } 560 outputStream.flush(); 561 } catch(IOException ioe) { 562 LOGGER.error("Damn exception when transferring bytes over sockets", ioe); 563 } 564 } catch (XmlaException xex) { 565 throw xex; 566 } catch (Exception ex) { 567 throw new XmlaException( 568 SERVER_FAULT_FC, 569 MSM_UNKNOWN_CODE, 570 MSM_UNKNOWN_FAULT_FS, 571 ex); 572 } 573 } 574 575 579 protected void handleFault( 580 HttpServletResponse response, 581 byte[][] responseSoapParts, 582 Phase phase, 583 Throwable t) { 584 585 response.reset(); 588 589 switch (phase) { 593 case VALIDATE_HTTP_HEAD: 594 response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); 595 break; 596 case INITIAL_PARSE: 597 case CALLBACK_PRE_ACTION: 598 case PROCESS_HEADER: 599 case PROCESS_BODY: 600 case CALLBACK_POST_ACTION: 601 case SEND_RESPONSE: 602 response.setStatus(HttpServletResponse.SC_OK); 603 break; 604 } 605 606 String code; 607 String faultCode; 608 String faultString; 609 String detail; 610 if (t instanceof XmlaException) { 611 XmlaException xex = (XmlaException) t; 612 code = xex.getCode(); 613 faultString = xex.getFaultString(); 614 faultCode = XmlaException.formatFaultCode(xex); 615 detail = XmlaException.formatDetail(xex.getDetail()); 616 617 } else { 618 t = XmlaException.getRootCause(t); 620 code = UNKNOWN_ERROR_CODE; 621 faultString = UNKNOWN_ERROR_FAULT_FS; 622 faultCode = XmlaException.formatFaultCode( 623 SERVER_FAULT_FC, code); 624 detail = XmlaException.formatDetail(t.getMessage()); 625 } 626 627 String encoding = response.getCharacterEncoding(); 628 629 ByteArrayOutputStream osBuf = new ByteArrayOutputStream (); 630 try { 631 SaxWriter writer = new DefaultSaxWriter(osBuf, encoding); 632 writer.startDocument(); 633 writer.startElement(SOAP_PREFIX +":Fault"); 634 635 writer.startElement("faultcode"); 640 writer.characters(faultCode); 641 writer.endElement(); 642 643 writer.startElement("faultstring"); 647 writer.characters(faultString); 648 writer.endElement(); 649 650 writer.startElement("faultactor"); 653 writer.characters(FAULT_ACTOR); 654 writer.endElement(); 655 656 if (phase != Phase.PROCESS_HEADER) { 664 writer.startElement("detail"); 665 writer.startElement(FAULT_NS_PREFIX +":error", new String [] { 666 "xmlns:" +FAULT_NS_PREFIX, MONDRIAN_NAMESPACE 667 }); 668 writer.startElement("code"); 669 writer.characters(code); 670 writer.endElement(); writer.startElement("desc"); 672 writer.characters(detail); 673 writer.endElement(); writer.endElement(); writer.endElement(); } 677 678 writer.endElement(); writer.endDocument(); 680 } catch (UnsupportedEncodingException uee) { 681 LOGGER.warn("This should be handled at begin of processing request", uee); 682 } catch (Exception e) { 683 LOGGER.error("Unexcepted runimt exception when handing SOAP fault :("); 684 } 685 686 responseSoapParts[1] = osBuf.toByteArray(); 687 } 688 689 } 690 691 | Popular Tags |