1 52 53 package freemarker.ext.jsp; 54 55 import java.beans.IntrospectionException ; 56 import java.io.ByteArrayInputStream ; 57 import java.io.FilterInputStream ; 58 import java.io.IOException ; 59 import java.io.InputStream ; 60 import java.net.MalformedURLException ; 61 import java.util.ArrayList ; 62 import java.util.HashMap ; 63 import java.util.Iterator ; 64 import java.util.List ; 65 import java.util.Map ; 66 import java.util.Set ; 67 import java.util.zip.ZipEntry ; 68 import java.util.zip.ZipInputStream ; 69 70 import javax.servlet.ServletContext ; 71 import javax.servlet.http.HttpServletRequest ; 72 import javax.xml.parsers.ParserConfigurationException ; 73 import javax.xml.parsers.SAXParserFactory ; 74 75 import org.xml.sax.Attributes ; 76 import org.xml.sax.EntityResolver ; 77 import org.xml.sax.InputSource ; 78 import org.xml.sax.Locator ; 79 import org.xml.sax.SAXException ; 80 import org.xml.sax.SAXParseException ; 81 import org.xml.sax.XMLReader ; 82 import org.xml.sax.helpers.DefaultHandler ; 83 84 import freemarker.core.Environment; 85 import freemarker.ext.servlet.FreemarkerServlet; 86 import freemarker.ext.servlet.HttpRequestHashModel; 87 import freemarker.log.Logger; 88 import freemarker.template.TemplateHashModel; 89 import freemarker.template.TemplateModel; 90 import freemarker.template.TemplateModelException; 91 import freemarker.template.utility.ClassUtil; 92 93 94 95 105 public class TaglibFactory implements TemplateHashModel { 106 private static final Logger logger = Logger.getLogger("freemarker.jsp"); 107 108 private static final int LOOKUP_NONE = 0; 110 private static final int LOOKUP_WEB_XML = 1; 112 private static final int LOOKUP_JARS = 2; 114 115 private final ServletContext ctx; 116 private final Map taglibs = new HashMap (); 117 private final Map locations = new HashMap (); 118 private int lookupPhase = LOOKUP_NONE; 119 120 126 public TaglibFactory(ServletContext ctx) { 127 this.ctx = ctx; 128 } 129 130 146 public TemplateModel get(String uri) throws TemplateModelException { 147 uri = resolveRelativeUri(uri); 148 synchronized (taglibs) { 149 Taglib taglib = null; 150 taglib = (Taglib) taglibs.get(uri); 151 if(taglib != null) { 152 return taglib; 153 } 154 155 taglib = new Taglib(); 156 try { 157 do { 158 if(taglib.load(uri, ctx, locations)) { 159 taglibs.put(uri, taglib); 160 return taglib; 161 } 162 } 163 while(getMoreTaglibLocations()); 164 } 165 catch(TemplateModelException e) { 166 throw e; 167 } 168 catch(Exception e) { 169 throw new TemplateModelException("Could not load taglib information", e); 170 } 171 return null; 172 } 173 } 174 175 178 public boolean isEmpty() { 179 return false; 180 } 181 182 private boolean getMoreTaglibLocations() throws MalformedURLException , ParserConfigurationException , IOException , SAXException 183 { 184 switch(lookupPhase) { 185 case LOOKUP_NONE: { 186 getLocationsFromWebXml(); 187 lookupPhase = LOOKUP_WEB_XML; 188 return true; 189 } 190 case LOOKUP_WEB_XML: { 191 getLocationsFromLibJars(); 192 lookupPhase = LOOKUP_JARS; 193 return true; 194 } 195 default : { 196 return false; 197 } 198 } 199 } 200 201 private void getLocationsFromWebXml() throws MalformedURLException , ParserConfigurationException , IOException , SAXException 202 { 203 WebXmlParser webXmlParser = new WebXmlParser(locations); 204 InputStream in = ctx.getResourceAsStream("/WEB-INF/web.xml"); 205 if (in == null) { 206 return; 208 } 209 try { 210 parseXml(in, ctx.getResource("/WEB-INF/web.xml").toExternalForm(), webXmlParser); 211 } 212 finally { 213 in.close(); 214 } 215 } 216 217 private static class WebXmlParser extends DefaultHandler { 218 private final Map locations; 219 220 private StringBuffer buf; 221 private String uri; 222 private String location; 223 224 WebXmlParser(Map locations) { 225 this.locations = locations; 226 } 227 228 public void startElement( 229 String nsuri, 230 String localName, 231 String qName, 232 Attributes atts) { 233 if ("taglib-uri".equals(qName) 234 || "taglib-location".equals(qName)) { 235 buf = new StringBuffer (); 236 } 237 } 238 239 public void characters(char[] chars, int off, int len) { 240 if (buf != null) { 241 buf.append(chars, off, len); 242 } 243 } 244 245 public void endElement(String nsuri, String localName, String qName) { 246 if ("taglib-uri".equals(qName)) { 247 uri = buf.toString().trim(); 248 buf = null; 249 } 250 else if ("taglib-location".equals(qName)) { 251 location = buf.toString().trim(); 252 if(location.indexOf("://") == -1 && !location.startsWith("/")) { 253 location = "/WEB-INF/" + location; 254 } 255 buf = null; 256 } 257 else if ("taglib".equals(qName)) { 258 String [] loc = new String [2]; 259 loc[0] = location; 260 if(location.endsWith(".jar") || location.endsWith(".zip")) { 261 loc[1] = "META-INF/taglib.tld"; 262 } 263 locations.put(uri, loc); 264 if(logger.isDebugEnabled()) { 265 logger.debug("web.xml assigned URI " + uri + " to location " + loc[0] + (loc[1] != null ? "!" + loc[1] : "")); 266 } 267 } 268 } 269 } 270 271 272 private void getLocationsFromLibJars() throws ParserConfigurationException , IOException , SAXException 273 { 274 Set libs = ctx.getResourcePaths("/WEB-INF/lib"); 275 for (Iterator iter = libs.iterator(); iter.hasNext();) { 276 String path = (String ) iter.next(); 277 if(path.endsWith(".jar") || path.endsWith(".zip")) { 278 ZipInputStream zin = new ZipInputStream (ctx.getResourceAsStream(path)); 279 InputStream uin = new FilterInputStream (zin) { 281 public void close() { 282 } 283 }; 284 try { 285 for(;;) { 286 ZipEntry ze = zin.getNextEntry(); 287 if(ze == null) { 288 break; 289 } 290 String zname = ze.getName(); 291 if(zname.startsWith("META-INF/") && zname.endsWith(".tld")) { 292 String url = "jar:" + 293 ctx.getResource(path).toExternalForm() + 294 "!" + zname; 295 String loc = getTldUri(uin, url); 296 if(loc != null) { 297 locations.put(loc, new String [] { path, zname }); 298 if(logger.isDebugEnabled()) { 299 logger.debug("libjar assigned URI " + loc + " to location " + path + "!" + zname); 300 } 301 } 302 } 303 } 304 } 305 finally { 306 zin.close(); 307 } 308 } 309 } 310 } 311 312 private String getTldUri(InputStream in, String url) throws ParserConfigurationException , IOException , SAXException 313 { 314 TldUriReader tur = new TldUriReader(); 315 parseXml(in, url, tur); 316 return tur.getUri(); 317 } 318 319 private static class TldUriReader extends DefaultHandler { 320 321 private StringBuffer buf; 322 private String uri; 323 324 TldUriReader() { 325 } 326 327 String getUri() { 328 return uri; 329 } 330 331 public void startElement( 332 String nsuri, 333 String localName, 334 String qName, 335 Attributes atts) { 336 if ("uri".equals(qName)) { 337 buf = new StringBuffer (); 338 } 339 } 340 341 public void characters(char[] chars, int off, int len) { 342 if (buf != null) { 343 buf.append(chars, off, len); 344 } 345 } 346 347 public void endElement(String nsuri, String localName, String qName) { 348 if ("uri".equals(qName)) { 349 uri = buf.toString().trim(); 350 buf = null; 351 } 352 } 353 } 354 355 private static void parseXml(InputStream in, String url, DefaultHandler handler) 356 throws 357 ParserConfigurationException , IOException , SAXException 358 { 359 InputSource is = new InputSource (); 360 is.setByteStream(in); 361 is.setSystemId(url); 362 SAXParserFactory factory = SAXParserFactory.newInstance(); 363 factory.setNamespaceAware(false); 364 factory.setValidating(true); 365 XMLReader reader = factory.newSAXParser().getXMLReader(); 366 reader.setEntityResolver(new LocalTaglibDtds()); 367 reader.setContentHandler(handler); 368 reader.parse(is); 369 } 370 371 private static final class Taglib implements TemplateHashModel { 372 private Map tags; 373 374 Taglib() { 375 } 376 377 public TemplateModel get(String key) { 378 return (TagTransformModel) tags.get(key); 379 } 380 381 public boolean isEmpty() { 382 return false; 383 } 384 385 boolean load(String uri, ServletContext ctx, Map locations) 386 throws 387 ParserConfigurationException , 388 IOException , 389 SAXException , 390 TemplateModelException 391 { 392 String [] tldPath = getTldPath(uri, locations); 393 if(logger.isDebugEnabled()) { 394 if(tldPath == null) { 395 logger.debug("Loading taglib " + uri + " from location null"); 396 } 397 else { 398 logger.debug("Loading taglib " + uri + " from location " + tldPath[0] + (tldPath[1] != null ? "!" + tldPath[1] : "")); 399 } 400 } 401 tags = loadTaglib(tldPath, ctx); 402 if(tags != null) { 403 locations.remove(uri); 404 return true; 405 } 406 else { 407 return false; 408 } 409 } 410 } 411 412 private static final Map loadTaglib(String [] tldPath, ServletContext ctx) 413 throws 414 ParserConfigurationException , IOException , SAXException , TemplateModelException 415 { 416 if (tldPath == null) { 417 return null; 418 } 419 String filePath = tldPath[0]; 420 TldParser tldParser = new TldParser(); 421 InputStream in = ctx.getResourceAsStream(filePath); 422 if(in == null) { 423 throw new TemplateModelException("Could not find webapp resource " + filePath); 424 } 425 String url = ctx.getResource(filePath).toExternalForm(); 426 try { 427 String jarPath = tldPath[1]; 428 if(jarPath != null) { 429 ZipInputStream zin = new ZipInputStream (in); 430 for(;;) { 431 ZipEntry ze = zin.getNextEntry(); 432 if(ze == null) { 433 throw new TemplateModelException("Could not find JAR entry " + jarPath + " inside webapp resource " + filePath); 434 } 435 String zname = ze.getName(); 436 if(zname.equals(jarPath)) { 437 parseXml(zin, "jar:" + url + "!" + zname, tldParser); 438 break; 439 } 440 } 441 } 442 else { 443 parseXml(in, url, tldParser); 444 } 445 } 446 finally { 447 in.close(); 448 } 449 EventForwarding eventForwarding = EventForwarding.getInstance(ctx); 450 if(eventForwarding != null) { 451 eventForwarding.addListeners(tldParser.getListeners()); 452 } 453 else if(tldParser.getListeners().size() > 0) { 454 throw new TemplateModelException( 455 "Event listeners specified in the TLD could not be " + 456 " registered since the web application doesn't have a" + 457 " listener of class " + EventForwarding.class.getName() + 458 ". To remedy this, add this element to web.xml:\n" + 459 "| <listener>\n" + 460 "| <listener-class>" + EventForwarding.class.getName() + "</listener-class>\n" + 461 "| </listener>"); 462 } 463 return tldParser.getTags(); 464 } 465 466 private static final String [] getTldPath(String uri, Map locations) 467 { 468 String [] path = (String [])locations.get(uri); 469 if(path != null) { 472 return path; 473 } 474 475 if(uri.startsWith("/")) { 478 path = new String [2]; 479 path[0] = uri; 480 if(uri.endsWith(".jar") || uri.endsWith(".zip")) { 481 path[1] = "META-INF/taglib.tld"; 482 } 483 return path; 484 } 485 486 return null; 487 } 488 489 private static String resolveRelativeUri(String uri) 490 throws 491 TemplateModelException 492 { 493 if(uri.startsWith("/") || uri.indexOf("://") != -1) { 495 return uri; 496 } 497 498 TemplateModel reqHash = 504 Environment.getCurrentEnvironment().getVariable( 505 FreemarkerServlet.KEY_REQUEST_PRIVATE); 506 if(reqHash instanceof HttpRequestHashModel) { 507 HttpServletRequest req = 508 ((HttpRequestHashModel)reqHash).getRequest(); 509 String pi = req.getPathInfo(); 510 String reqPath = req.getServletPath(); 511 if(reqPath == null) { 512 reqPath = ""; 513 } 514 reqPath += (pi == null ? "" : pi); 515 int lastSlash = reqPath.lastIndexOf('/'); 518 if(lastSlash != -1) { 519 return reqPath.substring(0, lastSlash + 1) + uri; 520 } 521 else { 522 return '/' + uri; 523 } 524 } 525 throw new TemplateModelException( 526 "Can't resolve relative URI " + uri + 527 " as request URL information is unavailable."); 528 } 529 530 private static final class TldParser extends DefaultHandler { 531 private final Map tags = new HashMap (); 532 private final List listeners = new ArrayList (); 533 534 private Locator locator; 535 private StringBuffer buf; 536 private String tagName; 537 private String tagClass; 538 539 Map getTags() { 540 return tags; 541 } 542 543 List getListeners() { 544 return listeners; 545 } 546 547 public void setDocumentLocator(Locator locator) { 548 this.locator = locator; 549 } 550 551 public void startElement( 552 String nsuri, 553 String localName, 554 String qName, 555 Attributes atts) { 556 if ("name".equals(qName) || "tagclass".equals(qName) || "tag-class".equals(qName) || "listener-class".equals(qName)) { 557 buf = new StringBuffer (); 558 } 559 } 560 561 public void characters(char[] chars, int off, int len) { 562 if (buf != null) { 563 buf.append(chars, off, len); 564 } 565 } 566 567 public void endElement(String nsuri, String localName, String qName) 568 throws SAXParseException { 569 if ("name".equals(qName)) { 570 if(tagName == null) { 571 tagName = buf.toString().trim(); 572 } 573 buf = null; 574 } 575 else if ("tagclass".equals(qName) || "tag-class".equals(qName)) { 576 tagClass = buf.toString().trim(); 577 buf = null; 578 } 579 else if ("tag".equals(qName)) { 580 try { 581 tags.put( 582 tagName, 583 new TagTransformModel(ClassUtil.forName(tagClass))); 584 tagName = null; 585 tagClass = null; 586 } 587 catch (IntrospectionException e) { 588 throw new SAXParseException ( 589 "Can't introspect tag class " + tagClass, 590 locator, 591 e); 592 } 593 catch (ClassNotFoundException e) { 594 throw new SAXParseException ( 595 "Can't find tag class " + tagClass, 596 locator, 597 e); 598 } 599 } 600 else if ("listener-class".equals(qName)) { 601 String listenerClass = buf.toString().trim(); 602 buf = null; 603 try { 604 listeners.add(ClassUtil.forName(listenerClass).newInstance()); 605 } 606 catch(Exception e) { 607 throw new SAXParseException ( 608 "Can't instantiate listener class " + listenerClass, 609 locator, 610 e); 611 } 612 } 613 } 614 } 615 616 private static final Map dtds = new HashMap (); 617 static 618 { 619 dtds.put("http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd", "web-jsptaglibrary_2_0.xsd"); 621 dtds.put("-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN", "web-jsptaglibrary_1_2.dtd"); 623 dtds.put("http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd", "web-jsptaglibrary_1_2.dtd"); 624 dtds.put("-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN", "web-jsptaglibrary_1_1.dtd"); 626 dtds.put("http://java.sun.com/j2ee/dtds/web-jsptaglibrary_1_1.dtd", "web-jsptaglibrary_1_1.dtd"); 627 dtds.put("http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd", "web-app_2_4.xsd"); 629 dtds.put("-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN", "web-app_2_3.dtd"); 631 dtds.put("http://java.sun.com/dtd/web-app_2_3.dtd", "web-app_2_3.dtd"); 632 dtds.put("-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN", "web-app_2_2.dtd"); 634 dtds.put("http://java.sun.com/j2ee/dtds/web-app_2_2.dtd", "web-app_2_2.dtd"); 635 } 636 private static final class LocalTaglibDtds implements EntityResolver { 637 public InputSource resolveEntity(String publicId, String systemId) 638 { 639 String resourceName = (String )dtds.get(publicId); 640 if(resourceName == null) 641 { 642 resourceName = (String )dtds.get(systemId); 643 } 644 InputStream resourceStream; 645 if(resourceName != null) 646 { 647 resourceStream = getClass().getResourceAsStream(resourceName); 648 } 649 else 650 { 651 resourceStream = new ByteArrayInputStream (new byte[0]); 653 } 654 InputSource is = new InputSource (); 655 is.setPublicId(publicId); 656 is.setSystemId(systemId); 657 is.setByteStream(resourceStream); 658 return is; 659 } 660 } 661 } 662 | Popular Tags |