1 12 package mondrian.xmla; 13 14 import mondrian.olap.Util; 15 import mondrian.spi.CatalogLocator; 16 import mondrian.spi.impl.ServletContextCatalogLocator; 17 18 import org.apache.log4j.Logger; 19 import org.eigenbase.xom.*; 20 import org.w3c.dom.Element ; 21 22 import javax.servlet.ServletContext ; 23 import javax.servlet.ServletConfig ; 24 import javax.servlet.ServletException ; 25 import javax.servlet.http.HttpServlet ; 26 import javax.servlet.http.HttpServletRequest ; 27 import javax.servlet.http.HttpServletResponse ; 28 import java.io.*; 29 import java.net.MalformedURLException ; 30 import java.net.URL ; 31 import java.util.*; 32 33 40 public abstract class XmlaServlet extends HttpServlet  41 implements XmlaConstants { 42 43 private static final Logger LOGGER = Logger.getLogger(XmlaServlet.class); 44 45 public static final String PARAM_DATASOURCES_CONFIG = "DataSourcesConfig"; 46 public static final String PARAM_OPTIONAL_DATASOURCE_CONFIG = 47 "OptionalDataSourceConfig"; 48 public static final String PARAM_CHAR_ENCODING = "CharacterEncoding"; 49 public static final String PARAM_CALLBACKS = "Callbacks"; 50 51 public static final String DEFAULT_DATASOURCE_FILE = "datasources.xml"; 52 53 public enum Phase { 54 VALIDATE_HTTP_HEAD, 55 INITIAL_PARSE, 56 CALLBACK_PRE_ACTION, 57 PROCESS_HEADER, 58 PROCESS_BODY, 59 CALLBACK_POST_ACTION, 60 SEND_RESPONSE, 61 SEND_ERROR 62 } 63 64 68 public static boolean getBooleanInitParameter( 69 ServletConfig servletConfig, 70 String paramName) { 71 String paramValue = servletConfig.getInitParameter(paramName); 72 return paramValue != null && Boolean.valueOf(paramValue); 73 } 74 75 public static boolean getParameter( 76 HttpServletRequest req, 77 String paramName) { 78 String paramValue = req.getParameter(paramName); 79 return paramValue != null && Boolean.valueOf(paramValue); 80 } 81 82 protected CatalogLocator catalogLocator = null; 83 protected DataSourcesConfig.DataSources dataSources = null; 84 protected XmlaHandler xmlaHandler = null; 85 protected String charEncoding = null; 86 private final List<XmlaRequestCallback> callbackList = 87 new ArrayList<XmlaRequestCallback>(); 88 89 public XmlaServlet() { 90 } 91 92 93 97 public void init(ServletConfig servletConfig) throws ServletException { 98 super.init(servletConfig); 99 100 initCharEncodingHandler(servletConfig); 102 103 initCallbacks(servletConfig); 105 106 this.catalogLocator = makeCatalogLocator(servletConfig); 110 111 DataSourcesConfig.DataSources dataSources = 112 makeDataSources(servletConfig); 113 addToDataSources(dataSources); 114 } 115 116 121 protected XmlaHandler getXmlaHandler() { 122 if (this.xmlaHandler == null) { 123 this.xmlaHandler = 124 new XmlaHandler(this.dataSources, this.catalogLocator, "cxmla"); 125 } 126 return this.xmlaHandler; 127 } 128 129 132 protected final void addCallback(XmlaRequestCallback callback) { 133 callbackList.add(callback); 134 } 135 136 141 protected final List<XmlaRequestCallback> getCallbacks() { 142 return Collections.unmodifiableList(callbackList); 143 } 144 145 146 150 protected void doPost( 151 HttpServletRequest request, 152 HttpServletResponse response) 153 throws ServletException , IOException { 154 155 Element [] requestSoapParts = new Element [2]; 158 159 byte[][] responseSoapParts = new byte[2][]; 164 165 Phase phase = Phase.VALIDATE_HTTP_HEAD; 166 167 try { 168 169 if (charEncoding != null) { 170 try { 171 request.setCharacterEncoding(charEncoding); 172 response.setCharacterEncoding(charEncoding); 173 } catch (UnsupportedEncodingException uee) { 174 charEncoding = null; 175 String msg = "Unsupported character encoding '" + 176 charEncoding + 177 "': " + 178 "Use default character encoding from HTTP client for now"; 179 LOGGER.warn(msg); 180 } 181 } 182 183 response.setContentType("text/xml"); 184 185 Map<String , String > context = new HashMap<String , String >(); 186 187 try { 188 if (LOGGER.isDebugEnabled()) { 189 LOGGER.debug("Invoking validate http header callbacks"); 190 } 191 for (XmlaRequestCallback callback : getCallbacks()) { 192 if (!callback.processHttpHeader( 193 request, 194 response, 195 context)) { 196 return; 197 } 198 } 199 200 } catch (XmlaException xex) { 201 LOGGER.error("Errors when invoking callbacks validateHttpHeader", xex); 202 handleFault(response, responseSoapParts, phase, xex); 203 phase = Phase.SEND_ERROR; 204 marshallSoapMessage(response, responseSoapParts); 205 return; 206 207 } catch (Exception ex) { 208 LOGGER.error("Errors when invoking callbacks validateHttpHeader", ex); 209 handleFault(response, responseSoapParts, 210 phase, new XmlaException( 211 SERVER_FAULT_FC, 212 CHH_CODE, 213 CHH_FAULT_FS, 214 ex)); 215 phase = Phase.SEND_ERROR; 216 marshallSoapMessage(response, responseSoapParts); 217 return; 218 } 219 220 221 phase = Phase.INITIAL_PARSE; 222 223 try { 224 if (LOGGER.isDebugEnabled()) { 225 LOGGER.debug("Unmarshalling SOAP message"); 226 } 227 228 String contentType = request.getContentType(); 230 if (contentType == null || 231 contentType.indexOf("text/xml") == -1) { 232 throw new IllegalArgumentException ("Only accepts content type 'text/xml', not '" + contentType + "'"); 233 } 234 235 unmarshallSoapMessage(request, requestSoapParts); 236 237 } catch (XmlaException xex) { 238 LOGGER.error("Unable to unmarshall SOAP message", xex); 239 handleFault(response, responseSoapParts, phase, xex); 240 phase = Phase.SEND_ERROR; 241 marshallSoapMessage(response, responseSoapParts); 242 return; 243 } 244 245 phase = Phase.PROCESS_HEADER; 246 247 try { 248 if (LOGGER.isDebugEnabled()) { 249 LOGGER.debug("Handling XML/A message header"); 250 } 251 252 handleSoapHeader(response, 254 requestSoapParts, 255 responseSoapParts, 256 context); 257 } catch (XmlaException xex) { 258 LOGGER.error("Errors when handling XML/A message", xex); 259 handleFault(response, responseSoapParts, phase, xex); 260 phase = Phase.SEND_ERROR; 261 marshallSoapMessage(response, responseSoapParts); 262 return; 263 } 264 265 phase = Phase.CALLBACK_PRE_ACTION; 266 267 268 try { 269 if (LOGGER.isDebugEnabled()) { 270 LOGGER.debug("Invoking callbacks preAction"); 271 } 272 273 for (XmlaRequestCallback callback : getCallbacks()) { 274 callback.preAction(request, requestSoapParts, context); 275 } 276 } catch (XmlaException xex) { 277 LOGGER.error("Errors when invoking callbacks preaction", xex); 278 handleFault(response, responseSoapParts, phase, xex); 279 phase = Phase.SEND_ERROR; 280 marshallSoapMessage(response, responseSoapParts); 281 return; 282 283 } catch (Exception ex) { 284 LOGGER.error("Errors when invoking callbacks preaction", ex); 285 handleFault(response, responseSoapParts, 286 phase, new XmlaException( 287 SERVER_FAULT_FC, 288 CPREA_CODE, 289 CPREA_FAULT_FS, 290 ex)); 291 phase = Phase.SEND_ERROR; 292 marshallSoapMessage(response, responseSoapParts); 293 return; 294 } 295 296 phase = Phase.PROCESS_BODY; 297 298 try { 299 if (LOGGER.isDebugEnabled()) { 300 LOGGER.debug("Handling XML/A message body"); 301 } 302 303 handleSoapBody(response, 305 requestSoapParts, 306 responseSoapParts, 307 context); 308 309 } catch (XmlaException xex) { 310 LOGGER.error("Errors when handling XML/A message", xex); 311 handleFault(response, responseSoapParts, phase, xex); 312 phase = Phase.SEND_ERROR; 313 marshallSoapMessage(response, responseSoapParts); 314 return; 315 } 316 317 phase = Phase.CALLBACK_POST_ACTION; 318 319 try { 320 if (LOGGER.isDebugEnabled()) { 321 LOGGER.debug("Invoking callbacks postAction"); 322 } 323 324 for (XmlaRequestCallback callback : getCallbacks()) { 325 callback.postAction( 326 request, response, 327 responseSoapParts, context); 328 } 329 } catch (XmlaException xex) { 330 LOGGER.error("Errors when invoking callbacks postaction", xex); 331 handleFault(response, responseSoapParts, phase, xex); 332 phase = Phase.SEND_ERROR; 333 marshallSoapMessage(response, responseSoapParts); 334 return; 335 336 } catch (Exception ex) { 337 LOGGER.error("Errors when invoking callbacks postaction", ex); 338 handleFault(response, responseSoapParts, 339 phase, new XmlaException( 340 SERVER_FAULT_FC, 341 CPOSTA_CODE, 342 CPOSTA_FAULT_FS, 343 ex)); 344 phase = Phase.SEND_ERROR; 345 marshallSoapMessage(response, responseSoapParts); 346 return; 347 } 348 349 phase = Phase.SEND_RESPONSE; 350 351 try { 352 353 response.setStatus(HttpServletResponse.SC_OK); 354 marshallSoapMessage(response, responseSoapParts); 355 356 } catch (XmlaException xex) { 357 LOGGER.error("Errors when handling XML/A message", xex); 358 handleFault(response, responseSoapParts, phase, xex); 359 phase = Phase.SEND_ERROR; 360 marshallSoapMessage(response, responseSoapParts); 361 return; 362 } 363 364 } catch (Throwable t) { 365 LOGGER.error("Unknown Error when handling XML/A message", t); 366 handleFault(response, responseSoapParts, phase, t); 367 marshallSoapMessage(response, responseSoapParts); 368 } 369 370 } 371 372 377 protected abstract void unmarshallSoapMessage( 378 HttpServletRequest request, 379 Element [] requestSoapParts) throws XmlaException; 380 381 386 protected abstract void handleSoapHeader( 387 HttpServletResponse response, 388 Element [] requestSoapParts, 389 byte[][] responseSoapParts, 390 Map<String , String > context) throws XmlaException; 391 392 397 protected abstract void handleSoapBody( 398 HttpServletResponse response, 399 Element [] requestSoapParts, 400 byte[][] responseSoapParts, 401 Map<String , String > context) throws XmlaException; 402 403 406 protected abstract void marshallSoapMessage( 407 HttpServletResponse response, 408 byte[][] responseSoapParts) throws XmlaException; 409 410 413 protected abstract void handleFault( 414 HttpServletResponse response, 415 byte[][] responseSoapParts, 416 Phase phase, 417 Throwable t); 418 419 420 421 424 protected CatalogLocator makeCatalogLocator(ServletConfig servletConfig) { 425 ServletContext servletContext = servletConfig.getServletContext(); 426 return new ServletContextCatalogLocator(servletContext); 427 } 428 429 443 protected DataSourcesConfig.DataSources makeDataSources( 444 ServletConfig servletConfig) { 445 446 String paramValue = 447 servletConfig.getInitParameter(PARAM_DATASOURCES_CONFIG); 448 boolean optional = 451 getBooleanInitParameter(servletConfig, PARAM_OPTIONAL_DATASOURCE_CONFIG); 452 453 URL dataSourcesConfigUrl = null; 454 try { 455 if (paramValue == null) { 456 String defaultDS = "WEB-INF/" + DEFAULT_DATASOURCE_FILE; 458 ServletContext servletContext = servletConfig.getServletContext(); 459 File realPath = new File(servletContext.getRealPath(defaultDS)); 460 if (realPath.exists()) { 461 dataSourcesConfigUrl = realPath.toURL(); 463 } 464 } else { 465 paramValue = Util.replaceProperties( 466 paramValue, 467 Util.toMap(System.getProperties())); 468 if (LOGGER.isDebugEnabled()) { 469 String msg = "XmlaServlet.makeDataSources: " + 470 "paramValue="+paramValue; 471 LOGGER.debug(msg); 472 } 473 MalformedURLException mue = null; 475 try { 476 dataSourcesConfigUrl = new URL (paramValue); 477 } catch (MalformedURLException e) { 478 mue = e; 480 } 481 if (dataSourcesConfigUrl == null) { 482 File f = new File(paramValue); 484 if (f.exists()) { 485 dataSourcesConfigUrl = f.toURL(); 487 } else if (mue != null) { 488 if (! optional) { 491 throw mue; 492 } 493 } 494 } 495 } 496 } catch (MalformedURLException mue) { 497 throw Util.newError(mue, "invalid URL path '" + paramValue + "'"); 498 } 499 500 if (LOGGER.isDebugEnabled()) { 501 String msg = "XmlaServlet.makeDataSources: " + 502 "dataSourcesConfigUrl="+dataSourcesConfigUrl; 503 LOGGER.debug(msg); 504 } 505 return (dataSourcesConfigUrl == null) 507 ? null : parseDataSourcesUrl(dataSourcesConfigUrl); 508 } 509 510 protected void addToDataSources(DataSourcesConfig.DataSources dataSources) { 511 if (this.dataSources == null) { 512 this.dataSources = dataSources; 513 } else if (dataSources != null) { 514 DataSourcesConfig.DataSource[] ds1 = this.dataSources.dataSources; 515 int len1 = ds1.length; 516 DataSourcesConfig.DataSource[] ds2 = dataSources.dataSources; 517 int len2 = ds2.length; 518 519 DataSourcesConfig.DataSource[] tmp = 520 new DataSourcesConfig.DataSource[len1+len2]; 521 522 System.arraycopy(ds1, 0, tmp, 0, len1); 523 System.arraycopy(ds2, 0, tmp, len1, len2); 524 525 this.dataSources.dataSources = tmp; 526 } else { 527 LOGGER.warn("XmlaServlet.addToDataSources: DataSources is null"); 528 } 529 } 530 531 protected DataSourcesConfig.DataSources parseDataSourcesUrl( 532 URL dataSourcesConfigUrl) { 533 534 try { 535 String dataSourcesConfigString = 536 Util.readURL( 537 dataSourcesConfigUrl, 538 Util.toMap(System.getProperties())); 539 return parseDataSources(dataSourcesConfigString); 540 541 } catch (Exception e) { 542 throw Util.newError(e, "Failed to parse data sources config '" + 543 dataSourcesConfigUrl.toExternalForm() + "'"); 544 } 545 } 546 protected DataSourcesConfig.DataSources parseDataSources( 547 String dataSourcesConfigString) { 548 549 try { 550 if (dataSourcesConfigString == null) { 551 LOGGER.warn("XmlaServlet.parseDataSources: null input"); 552 return null; 553 } 554 dataSourcesConfigString = 555 Util.replaceProperties( 556 dataSourcesConfigString, 557 Util.toMap(System.getProperties())); 558 559 if (LOGGER.isDebugEnabled()) { 560 String msg = "XmlaServlet.parseDataSources: " + 561 "dataSources="+dataSourcesConfigString; 562 LOGGER.debug(msg); 563 } 564 final Parser parser = XOMUtil.createDefaultParser(); 565 final DOMWrapper doc = parser.parse(dataSourcesConfigString); 566 return new DataSourcesConfig.DataSources(doc); 567 568 } catch (XOMException e) { 569 throw Util.newError(e, "Failed to parse data sources config: " + 570 dataSourcesConfigString); 571 } 572 } 573 574 577 protected void initCharEncodingHandler(ServletConfig servletConfig) { 578 String paramValue = servletConfig.getInitParameter(PARAM_CHAR_ENCODING); 579 if (paramValue != null) { 580 this.charEncoding = paramValue; 581 } else { 582 this.charEncoding = null; 583 LOGGER.warn("Use default character encoding from HTTP client"); 584 } 585 } 586 587 590 protected void initCallbacks(ServletConfig servletConfig) { 591 String callbacksValue = servletConfig.getInitParameter(PARAM_CALLBACKS); 592 593 if (callbacksValue != null) { 594 String [] classNames = callbacksValue.split(";"); 595 596 int count = 0; 597 nextCallback: 598 for (String className1 : classNames) { 599 String className = className1.trim(); 600 601 try { 602 Class <?> cls = Class.forName(className); 603 if (XmlaRequestCallback.class.isAssignableFrom(cls)) { 604 XmlaRequestCallback callback = 605 (XmlaRequestCallback) cls.newInstance(); 606 607 try { 608 callback.init(servletConfig); 609 } catch (Exception e) { 610 LOGGER.warn("Failed to initialize callback '" + 611 className + "'", e); 612 continue nextCallback; 613 } 614 615 addCallback(callback); 616 count++; 617 618 if (LOGGER.isDebugEnabled()) { 619 LOGGER.info("Register callback '" + 620 className + "'"); 621 } 622 } else { 623 LOGGER.warn("'" + className + 624 "' is not an implementation of '" + 625 XmlaRequestCallback.class + "'"); 626 } 627 } catch (ClassNotFoundException cnfe) { 628 LOGGER.warn("Callback class '" + className + "' not found", 629 cnfe); 630 } catch (InstantiationException ie) { 631 LOGGER.warn("Can't instantiate class '" + className + "'", 632 ie); 633 } catch (IllegalAccessException iae) { 634 LOGGER.warn("Can't instantiate class '" + className + "'", 635 iae); 636 } 637 } 638 LOGGER.debug("Registered " + count + " callback" + (count > 1 ? "s" : "")); 639 } 640 } 641 642 } 643 644 | Popular Tags |