1 29 30 package com.caucho.jsp; 31 32 import com.caucho.java.JavaCompiler; 33 import com.caucho.java.LineMap; 34 import com.caucho.log.Log; 35 import com.caucho.server.connection.CauchoRequest; 36 import com.caucho.server.connection.CauchoResponse; 37 import com.caucho.server.connection.RequestAdapter; 38 import com.caucho.server.connection.ResponseAdapter; 39 import com.caucho.server.dispatch.ServletConfigImpl; 40 import com.caucho.server.webapp.WebApp; 41 import com.caucho.util.Base64; 42 import com.caucho.util.CharBuffer; 43 import com.caucho.util.RegistryException; 44 import com.caucho.util.Semaphore; 45 import com.caucho.util.TimeUnit; 46 import com.caucho.vfs.Depend; 47 import com.caucho.vfs.Path; 48 import com.caucho.vfs.PersistentDependency; 49 import com.caucho.vfs.ReadStream; 50 import com.caucho.vfs.Vfs; 51 import com.caucho.vfs.WriteStream; 52 import com.caucho.xml.CauchoDocument; 53 import com.caucho.xml.Html; 54 import com.caucho.xml.Xml; 55 import com.caucho.xml.XmlParser; 56 import com.caucho.xml.XmlUtil; 57 import com.caucho.xpath.XPath; 58 import com.caucho.xpath.XPathException; 59 import com.caucho.xsl.CauchoStylesheet; 60 import com.caucho.xsl.StylesheetImpl; 61 import com.caucho.xsl.TransformerImpl; 62 import com.caucho.xsl.XslParseException; 63 64 import org.w3c.dom.Document ; 65 import org.w3c.dom.ProcessingInstruction ; 66 67 import javax.servlet.ServletException ; 68 import javax.servlet.ServletRequest ; 69 import javax.servlet.ServletResponse ; 70 import javax.servlet.http.HttpServletRequest ; 71 import javax.servlet.http.HttpServletResponse ; 72 import javax.servlet.jsp.JspException ; 73 import javax.servlet.jsp.JspFactory ; 74 import javax.servlet.jsp.PageContext ; 75 import javax.xml.transform.OutputKeys ; 76 import javax.xml.transform.Templates ; 77 import javax.xml.transform.TransformerConfigurationException ; 78 import javax.xml.transform.dom.DOMSource ; 79 import javax.xml.transform.stream.StreamResult ; 80 import java.io.FileNotFoundException ; 81 import java.io.IOException ; 82 import java.lang.ref.SoftReference ; 83 import java.util.ArrayList ; 84 import java.util.HashMap ; 85 import java.util.Iterator ; 86 import java.util.Properties ; 87 import java.util.logging.Level ; 88 import java.util.logging.Logger ; 89 90 91 94 class XtpPage extends Page { 95 private static final Logger log = Log.open(XtpPage.class); 96 97 private boolean _strictXml; 98 private boolean _toLower = true; 99 private boolean _entitiesAsText = false; 100 101 private Path _sourcePath; 102 private Path _pwd; 103 104 private String _uri; 105 private String _className; 106 private String _errorPage; 107 108 private WebApp _webApp; 109 110 private XslManager _xslManager; 111 112 private Page _page; 113 114 private HashMap <String ,SoftReference <Page>> _varyMap; 115 private ArrayList <String > _paramNames; 116 117 private JspManager _jspManager; 118 119 private final Semaphore _compileSemaphore = new Semaphore(1, false); 120 121 132 XtpPage(Path path, String uri, String className, 133 WebApp app, 134 XslManager xslManager, boolean strictXml) 135 throws ServletException , RegistryException 136 { 137 _sourcePath = path; 138 _sourcePath.setUserPath(uri); 139 _pwd = _sourcePath.getParent(); 140 _className = className; 141 _webApp = app; 142 _strictXml = strictXml; 143 _xslManager = xslManager; 144 _uri = uri; 145 146 ServletConfigImpl config = new ServletConfigImpl(); 147 config.setServletContext(_webApp); 148 149 init(config); 150 } 151 152 155 void setManager(JspManager manager) 156 { 157 _jspManager = manager; 158 } 159 160 163 void setHtmlToLower(boolean toLower) 164 { 165 _toLower = toLower; 166 } 167 168 171 void setEntitiesAsText(boolean entitiesAsText) 172 { 173 _entitiesAsText = entitiesAsText; 174 } 175 176 179 public boolean _caucho_isModified() 180 { 181 return false; 182 } 183 184 190 public void service(ServletRequest request, ServletResponse response) 191 throws IOException , ServletException 192 { 193 CauchoRequest req; 194 195 if (request instanceof CauchoRequest) 196 req = (CauchoRequest) request; 197 else 198 req = RequestAdapter.create((HttpServletRequest ) request, _webApp); 199 200 CauchoResponse res; 201 ResponseAdapter resAdapt = null; 202 203 if (response instanceof CauchoResponse) 204 res = (CauchoResponse) response; 205 else { 206 resAdapt = ResponseAdapter.create((HttpServletResponse ) response); 207 res = resAdapt; 208 } 209 210 try { 211 service(req, res); 212 } catch (InterruptedException e) { 213 log.log(Level.FINE, e.toString(), e); 214 215 log.warning("XTP: interrupted for " + req.getPageURI()); 216 217 res.sendError(503, "Server busy: XTP generation delayed"); 218 } finally { 219 if (resAdapt != null) 220 resAdapt.close(); 221 } 222 } 223 224 225 231 public void service(CauchoRequest req, CauchoResponse res) 232 throws IOException , ServletException , InterruptedException 233 { 234 Page page = getPage(req, res); 235 236 if (page != null) { 237 page.pageservice(req, res); 238 } 239 else { 240 log.warning("XTP: server busy on " + req.getPageURI()); 241 242 res.setHeader("Retry-After", "15"); 243 res.sendError(503, "Server busy: XTP generation delayed"); 244 } 245 } 246 247 250 private Page getPage(CauchoRequest req, CauchoResponse res) 251 throws IOException , ServletException , InterruptedException 252 { 253 String ss = null; 254 String varyName = null; 255 Page page = _page; 256 Page deadPage = null; 257 258 if (page == null) { 259 if (_varyMap != null) { 260 varyName = generateVaryName(req); 261 262 if (varyName != null) { 263 SoftReference <Page> ref = _varyMap.get(varyName); 264 page = ref != null ? ref.get() : null; 265 } 266 } 267 } 268 269 if (page != null && ! page.cauchoIsModified()) 270 return page; 271 272 deadPage = page; 273 page = null; 274 275 long timeout = deadPage == null ? 30L : 5L; 276 277 Thread.interrupted(); 278 if (_compileSemaphore.tryAcquire(timeout, TimeUnit.SECONDS)) { 279 try { 280 varyName = generateVaryName(req); 281 282 page = getPrecompiledPage(req, varyName); 283 284 if (page == null) { 285 CauchoDocument doc; 286 287 try { 288 doc = parseXtp(); 289 } catch (FileNotFoundException e) { 290 res.sendError(404); 291 throw e; 292 } 293 294 Templates stylesheet = compileStylesheet(req, doc); 295 296 varyName = generateVaryName(req); 298 299 page = getPrecompiledPage(req, varyName); 300 301 if (page == null) 302 page = compileJspPage(req, res, doc, stylesheet, varyName); 303 } 304 305 if (page != null) { 306 ServletConfigImpl config = new ServletConfigImpl(); 307 config.setServletContext(_webApp); 308 309 page.init(config); 310 311 if (varyName != null && _varyMap == null) 312 _varyMap = new HashMap <String ,SoftReference <Page>>(8); 313 314 if (varyName != null) 315 _varyMap.put(varyName, new SoftReference <Page>(page)); 316 else 317 _page = page; 318 } 319 else if (deadPage != null) { 320 _page = null; 321 322 if (varyName != null && _varyMap != null) 323 _varyMap.remove(varyName); 324 } 325 } finally { 326 _compileSemaphore.release(); 327 } 328 } 329 else { 330 log.warning("XTP: semaphore timed out on " + req.getPageURI()); 331 } 332 333 334 if (page != null) 335 return page; 336 else 337 return deadPage; 338 } 339 340 347 private Page getPrecompiledPage(CauchoRequest req, String varyName) 348 throws IOException , ServletException 349 { 350 Page page = null; 351 352 String className = getClassName(varyName); 353 354 try { 355 page = _jspManager.preload(className, 356 _webApp.getClassLoader(), 357 _webApp.getAppDir()); 358 359 if (page != null) { 360 if (log.isLoggable(Level.FINE)) 361 log.fine("XTP using precompiled page " + className); 362 363 return page; 364 } 365 } catch (Throwable e) { 366 log.log(Level.FINE, e.toString(), e); 367 } 368 369 return null; 370 } 371 372 375 private CauchoDocument parseXtp() 376 throws IOException , ServletException 377 { 378 ReadStream is = _sourcePath.openRead(); 379 try { 380 XmlParser parser; 381 382 if (_strictXml) { 383 parser = new Xml(); 384 parser.setEntitiesAsText(_entitiesAsText); 385 } 386 else { 387 parser = new Html(); 388 parser.setAutodetectXml(true); 389 parser.setEntitiesAsText(true); 390 parser.setToLower(_toLower); 392 } 393 394 parser.setResinInclude(true); 395 parser.setJsp(true); 396 397 return (CauchoDocument) parser.parseDocument(is); 398 } catch (Exception e) { 399 JspParseException jspE = JspParseException.create(e); 400 401 jspE.setErrorPage(_errorPage); 402 403 throw jspE; 404 } finally { 405 is.close(); 406 } 407 } 408 409 418 private Templates compileStylesheet(CauchoRequest req, CauchoDocument doc) 419 throws IOException , ServletException 420 { 421 String ssName = (String ) req.getAttribute("caucho.xsl.stylesheet"); 422 423 Templates stylesheet = null; 424 425 try { 426 if (ssName == null) 427 ssName = getStylesheetHref(doc, null); 428 429 stylesheet = _xslManager.get(ssName, req); 430 } catch (XslParseException e) { 431 JspParseException jspE; 432 if (e.getException() != null) 433 jspE = new JspParseException(e.getException()); 434 else 435 jspE = new JspParseException(e); 436 437 jspE.setErrorPage(_errorPage); 438 439 throw jspE; 440 } catch (Exception e) { 441 JspParseException jspE; 442 443 jspE = new JspParseException(e); 444 445 jspE.setErrorPage(_errorPage); 446 447 throw jspE; 448 } 449 450 ArrayList <String > params = null; 451 if (stylesheet instanceof StylesheetImpl) { 452 StylesheetImpl ss = (StylesheetImpl) stylesheet; 453 params = (ArrayList ) ss.getProperty(CauchoStylesheet.GLOBAL_PARAM); 454 } 455 456 for (int i = 0; params != null && i < params.size(); i++) { 457 String param = params.get(i); 458 459 if (_paramNames == null) 460 _paramNames = new ArrayList <String >(); 461 462 if (param.equals("xtp:context_path") || 463 param.equals("xtp:servlet_path")) 464 continue; 465 466 if (! _paramNames.contains(param)) 467 _paramNames.add(param); 468 } 469 470 return stylesheet; 471 } 472 473 483 private Page compileJspPage(CauchoRequest req, 484 CauchoResponse res, 485 CauchoDocument doc, 486 Templates stylesheet, 487 String varyName) 488 throws IOException , ServletException 489 { 490 varyName = generateVaryName(req); 492 493 String className = getClassName(varyName); 494 495 try { 496 return getJspPage(doc, stylesheet, req, res, className); 497 } catch (TransformerConfigurationException e) { 498 throw new ServletException (e); 499 } catch (JspException e) { 500 throw new ServletException (e); 501 } 502 } 503 504 507 private String getClassName(String varyName) 508 { 509 if (varyName == null) 510 return _className; 511 else 512 return _className + JavaCompiler.mangleName("?" + varyName); 513 } 514 515 528 private String generateVaryName(CauchoRequest req) 529 { 530 CharBuffer cb = CharBuffer.allocate(); 531 532 String ss = (String ) req.getAttribute("caucho.xsl.stylesheet"); 533 534 if (ss == null && (_paramNames == null || _paramNames.size() == 0)) 535 return null; 536 537 if (ss != null) { 538 cb.append("ss."); 539 cb.append(ss); 540 } 541 542 for (int i = 0; _paramNames != null && i < _paramNames.size(); i++) { 543 String name = (String ) _paramNames.get(i); 544 545 String value; 546 547 if (name.equals("xtp:path_info")) 548 value = req.getPathInfo(); 549 else 550 value = req.getParameter(name); 551 552 cb.append("."); 553 cb.append(name); 554 555 if (value != null) { 556 cb.append("."); 557 cb.append(value); 558 } 559 } 560 561 if (cb.length() == 0) 562 return null; 563 564 if (cb.length() < 64) 565 return cb.close(); 566 567 long hash = 37; 568 for (int i = 0; i < cb.length(); i++) 569 hash = 65521 * hash + cb.charAt(i); 570 571 cb.setLength(32); 572 Base64.encode(cb, hash); 573 574 return cb.close(); 575 } 576 577 588 private Page getJspPage(CauchoDocument doc, Templates stylesheet, 589 CauchoRequest req, CauchoResponse res, 590 String className) 591 throws IOException , ServletException , JspException , TransformerConfigurationException 592 { 593 Path workDir = _jspManager.getClassDir(); 594 String fullClassName = className; 595 Path path = workDir.lookup(fullClassName.replace('.', '/') + ".jsp"); 596 path.getParent().mkdirs(); 597 598 Properties output = stylesheet.getOutputProperties(); 599 600 String encoding = (String ) output.get(OutputKeys.ENCODING); 601 String mimeType = (String ) output.get(OutputKeys.MEDIA_TYPE); 602 String method = (String ) output.get(OutputKeys.METHOD); 603 604 if (method == null || encoding != null) { 605 } 606 else if (method.equals("xml")) 607 encoding = "UTF-8"; 608 609 javax.xml.transform.Transformer transformer; 610 transformer = stylesheet.newTransformer(); 611 612 for (int i = 0; _paramNames != null && i < _paramNames.size(); i++) { 613 String param = (String ) _paramNames.get(i); 614 615 transformer.setParameter(param, req.getParameter(param)); 616 } 617 618 String contextPath = req.getContextPath(); 619 if (contextPath != null && ! contextPath.equals("")) 620 transformer.setParameter("xtp:context_path", contextPath); 621 622 String servletPath = req.getServletPath(); 623 if (servletPath != null && ! servletPath.equals("")) 624 transformer.setParameter("xtp:servlet_path", servletPath); 625 626 String pathInfo = req.getPathInfo(); 627 if (pathInfo != null && ! pathInfo.equals("")) 628 transformer.setParameter("xtp:path_info", pathInfo); 629 630 transformer.setOutputProperty("caucho.jsp", "true"); 631 632 LineMap lineMap = null; 633 WriteStream os = path.openWrite(); 634 try { 635 if (encoding != null) { 636 os.setEncoding(encoding); 637 if (mimeType == null) 638 mimeType = "text/html"; 639 640 os.print("<%@ page contentType=\"" + mimeType + "; charset=" + 641 encoding + "\" %>"); 642 } 643 else if (mimeType != null) 644 os.print("<%@ page contentType=\"" + mimeType + "\" %>"); 645 646 lineMap = writeJspDoc(os, doc, transformer, req, res); 647 } finally { 648 os.close(); 649 } 650 651 StylesheetImpl ss = null; 652 if (stylesheet instanceof StylesheetImpl) 653 ss = (StylesheetImpl) stylesheet; 654 655 try { 656 path.setUserPath(_sourcePath.getPath()); 657 658 boolean cacheable = true; ArrayList <PersistentDependency> depends = 660 new ArrayList <PersistentDependency>(); 661 662 ArrayList <Depend> styleDepends = null; 663 if (ss != null) 664 styleDepends = (ArrayList ) ss.getProperty(StylesheetImpl.DEPENDS); 665 for (int i = 0; styleDepends != null && i < styleDepends.size(); i++) { 666 Depend depend = styleDepends.get(i); 667 668 Depend jspDepend = new Depend(depend.getPath(), 669 depend.getLastModified(), 670 depend.getLength()); 671 jspDepend.setRequireSource(true); 672 673 if (! depends.contains(jspDepend)) 674 depends.add(jspDepend); 675 } 676 677 ArrayList <Path> docDepends; 680 docDepends = (ArrayList ) doc.getProperty(CauchoDocument.DEPENDS); 681 for (int i = 0; docDepends != null && i < docDepends.size(); i++) { 682 Path depend = docDepends.get(i); 683 684 Depend jspDepend = new Depend(depend); 685 if (! depends.contains(jspDepend)) 686 depends.add(jspDepend); 687 } 688 689 ArrayList <Path> cacheDepends = null; 691 TransformerImpl xform = null; 692 if (transformer instanceof TransformerImpl) 693 xform = (TransformerImpl) transformer; 694 if (xform != null) 695 cacheDepends = (ArrayList ) xform.getProperty(TransformerImpl.CACHE_DEPENDS); 696 for (int i = 0; cacheDepends != null && i < cacheDepends.size(); i++) { 697 Path depend = cacheDepends.get(i); 698 Depend jspDepend = new Depend(depend); 699 if (! depends.contains(jspDepend)) 700 depends.add(jspDepend); 701 } 702 703 Page page = _jspManager.createGeneratedPage(path, _uri, className, depends); 704 705 return page; 706 } catch (IOException e) { 707 throw e; 708 } catch (ServletException e) { 709 throw e; 710 } catch (Exception e) { 711 throw new QJspException(e); 712 } 713 } 714 715 726 private LineMap writeJspDoc(WriteStream os, 727 Document doc, 728 javax.xml.transform.Transformer transformer, 729 CauchoRequest req, 730 CauchoResponse res) 731 throws IOException , ServletException 732 { 733 PageContext pageContext; 734 735 JspFactory factory = JspFactory.getDefaultFactory(); 736 737 TransformerImpl xform = null; 738 if (transformer instanceof TransformerImpl) 739 xform = (TransformerImpl) transformer; 740 String errorPage = null; 741 if (xform != null) 742 errorPage = (String ) xform.getProperty("caucho.error.page"); 743 pageContext = factory.getPageContext(this, 744 req, res, 745 errorPage, 746 false, 747 8192, false); 750 try { 751 if (xform != null) { 752 xform.setProperty("caucho.page.context", pageContext); 753 xform.setProperty("caucho.pwd", Vfs.lookup()); 754 } 755 756 DOMSource source = new DOMSource (doc); 757 StreamResult result = new StreamResult (os); 758 759 xform.setFeature(TransformerImpl.GENERATE_LOCATION, true); 760 transformer.transform(source, result); 761 762 if (xform != null) 763 return (LineMap) xform.getProperty(TransformerImpl.LINE_MAP); 764 else 765 return null; 766 } catch (Exception e) { 767 pageContext.handlePageException(e); 768 } finally { 769 factory.releasePageContext(pageContext); 770 } 771 772 return null; 773 } 774 775 789 private String getStylesheetHref(Document doc, String media) 790 throws XPathException 791 { 792 Iterator iter = XPath.select("//processing-instruction('xml-stylesheet')", 793 doc); 794 while (iter.hasNext()) { 795 ProcessingInstruction pi = (ProcessingInstruction ) iter.next(); 796 String value = pi.getNodeValue(); 797 String piMedia = XmlUtil.getPIAttribute(value, "media"); 798 799 if (piMedia == null || piMedia.equals(media)) 800 return XmlUtil.getPIAttribute(value, "href"); 801 } 802 803 return "default.xsl"; } 805 806 810 private boolean varyMedia(Document doc) 811 throws XPathException 812 { 813 Iterator iter = XPath.select("//processing-instruction('xml-stylesheet')", 814 doc); 815 while (iter.hasNext()) { 816 ProcessingInstruction pi = (ProcessingInstruction ) iter.next(); 817 String value = pi.getNodeValue(); 818 String piMedia = XmlUtil.getPIAttribute(value, "media"); 819 820 if (piMedia != null) 821 return true; 822 } 823 824 return false; 825 } 826 827 public boolean disableLog() 828 { 829 return true; 830 } 831 832 835 public String toString() 836 { 837 return "XtpPage[" + _uri + "]"; 838 } 839 } 840 | Popular Tags |