View Javadoc
1 /* 2 * $Header: /home/cvs/jakarta-commons-sandbox/jelly/src/java/org/apache/commons/jelly/JellyContext.java,v 1.10 2002/04/26 12:20:12 jstrachan Exp $ 3 * $Revision: 1.10 $ 4 * $Date: 2002/04/26 12:20:12 $ 5 * 6 * ==================================================================== 7 * 8 * The Apache Software License, Version 1.1 9 * 10 * Copyright (c) 1999-2002 The Apache Software Foundation. All rights 11 * reserved. 12 * 13 * Redistribution and use in source and binary forms, with or without 14 * modification, are permitted provided that the following conditions 15 * are met: 16 * 17 * 1. Redistributions of source code must retain the above copyright 18 * notice, this list of conditions and the following disclaimer. 19 * 20 * 2. Redistributions in binary form must reproduce the above copyright 21 * notice, this list of conditions and the following disclaimer in 22 * the documentation and/or other materials provided with the 23 * distribution. 24 * 25 * 3. The end-user documentation included with the redistribution, if 26 * any, must include the following acknowlegement: 27 * "This product includes software developed by the 28 * Apache Software Foundation (http://www.apache.org/)." 29 * Alternately, this acknowlegement may appear in the software itself, 30 * if and wherever such third-party acknowlegements normally appear. 31 * 32 * 4. The names "The Jakarta Project", "Commons", and "Apache Software 33 * Foundation" must not be used to endorse or promote products derived 34 * from this software without prior written permission. For written 35 * permission, please contact apache@apache.org. 36 * 37 * 5. Products derived from this software may not be called "Apache" 38 * nor may "Apache" appear in their names without prior written 39 * permission of the Apache Group. 40 * 41 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 42 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 43 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 44 * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR 45 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 46 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 47 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 48 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 49 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 50 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 51 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 52 * SUCH DAMAGE. 53 * ==================================================================== 54 * 55 * This software consists of voluntary contributions made by many 56 * individuals on behalf of the Apache Software Foundation. For more 57 * information on the Apache Software Foundation, please see 58 * <http://www.apache.org/>;. 59 * 60 * $Id: JellyContext.java,v 1.10 2002/04/26 12:20:12 jstrachan Exp $ 61 */ 62 package org.apache.commons.jelly; 63 64 import java.io.File; 65 import java.io.InputStream; 66 import java.net.MalformedURLException; 67 import java.net.URL; 68 import java.util.Hashtable; 69 import java.util.Iterator; 70 import java.util.Map; 71 72 import org.apache.commons.jelly.parser.XMLParser; 73 import org.apache.commons.logging.Log; 74 import org.apache.commons.logging.LogFactory; 75 76 /*** <p><code>JellyContext</code> represents the Jelly context.</p> 77 * 78 * @author <a href="mailto:jstrachan@apache.org">James Strachan</a> 79 * @version $Revision: 1.10 $ 80 */ 81 public class JellyContext { 82 83 /*** The root URL context (where scripts are located from) */ 84 private URL rootURL; 85 86 /*** The current URL context (where relative scripts are located from) */ 87 private URL currentURL; 88 89 /*** Tag libraries found so far */ 90 private Map taglibs = new Hashtable(); 91 92 /*** synchronized access to the variables in scope */ 93 private Map variables = new Hashtable(); 94 95 /*** The Log to which logging calls will be made. */ 96 private Log log = LogFactory.getLog(JellyContext.class); 97 98 /*** The parent context */ 99 private JellyContext parent; 100 101 /*** Do we inherit variables from parent context? */ 102 private boolean inherit = false; 103 104 /*** Do we export our variables to parent context? */ 105 private boolean export = false; 106 107 /*** Should we export tag libraries to our parents context */ 108 private boolean exportLibraries = true; 109 110 /*** Should we cache Tag instances, per thread, to reduce object contruction overhead? */ 111 private boolean cacheTags = false; 112 113 114 /*** 115 * The class loader to use for instantiating application objects. 116 * If not specified, the context class loader, or the class loader 117 * used to load this class itself, is used, based on the value of the 118 * <code>useContextClassLoader</code> variable. 119 */ 120 protected ClassLoader classLoader; 121 122 /*** 123 * Do we want to use the Context ClassLoader when loading classes 124 * for instantiating new objects? Default is <code>false</code>. 125 */ 126 protected boolean useContextClassLoader = false; 127 128 129 public JellyContext() { 130 this.currentURL = rootURL; 131 init(); 132 } 133 134 public JellyContext(URL rootURL) { 135 this( rootURL, rootURL ); 136 } 137 138 public JellyContext(URL rootURL, URL currentURL) { 139 this.rootURL = rootURL; 140 this.currentURL = currentURL; 141 init(); 142 } 143 144 public JellyContext(JellyContext parent) { 145 this.parent = parent; 146 this.rootURL = parent.rootURL; 147 this.currentURL = parent.currentURL; 148 this.variables.put("parentScope", parent.variables); 149 this.cacheTags = cacheTags; 150 init(); 151 } 152 153 public JellyContext(JellyContext parentJellyContext, URL currentURL) { 154 this(parentJellyContext); 155 this.currentURL = currentURL; 156 } 157 158 public JellyContext(JellyContext parentJellyContext, URL rootURL, URL currentURL) { 159 this(parentJellyContext, currentURL); 160 this.rootURL = rootURL; 161 } 162 163 private void init() { 164 variables.put("context", this); 165 variables.put("systemScope", System.getProperties()); 166 } 167 168 /*** 169 * @return the parent context for this context 170 */ 171 public JellyContext getParent() { 172 return parent; 173 } 174 175 /*** 176 * @return the scope of the given name, such as the 'parent' scope. 177 * If Jelly is used in a Servlet situation then 'request', 'session' and 'application' are other names 178 * for scopes 179 */ 180 public JellyContext getScope(String name) { 181 if ( "parent".equals( name ) ) { 182 return getParent(); 183 } 184 return null; 185 } 186 187 /*** 188 * Finds the variable value of the given name in this context or in any other parent context. 189 * If this context does not contain the variable, then its parent is used and then its parent 190 * and so forth until the context with no parent is found. 191 * 192 * @return the value of the variable in this or one of its descendant contexts or null 193 * if the variable could not be found. 194 */ 195 public Object findVariable(String name) { 196 Object answer = variables.get(name); 197 if ( answer == null && parent != null ) { 198 answer = parent.findVariable(name); 199 } 200 // ### this is a hack - remove this when we have support for pluggable Scopes 201 if ( answer == null ) { 202 try { 203 answer = System.getProperty(name); 204 } 205 catch (Throwable t) { 206 // ignore security exceptions 207 } 208 } 209 return answer; 210 } 211 212 213 /*** @return the value of the given variable name */ 214 public Object getVariable(String name) { 215 Object value = variables.get(name); 216 217 if ( value == null 218 && 219 isInherit() ) { 220 value = getParent().findVariable( name ); 221 } 222 223 return value; 224 } 225 226 /*** 227 * @return the value of the given variable name in the given variable scope 228 * @param name is the name of the variable 229 * @param scopeName is the optional scope name such as 'parent'. For servlet environments 230 * this could be 'application', 'session' or 'request'. 231 */ 232 public Object getVariable(String name, String scopeName) { 233 JellyContext scope = getScope(scopeName); 234 if ( scope != null ) { 235 return scope.getVariable(name); 236 } 237 return null; 238 } 239 240 241 242 /*** Sets the value of the given variable name */ 243 public void setVariable(String name, Object value) { 244 if ( isExport() ) { 245 getParent().setVariable( name, value ); 246 return; 247 } 248 if (value == null) { 249 variables.remove(name); 250 } 251 else { 252 variables.put(name, value); 253 } 254 } 255 256 /*** 257 * Sets the value of the given variable name in the given variable scope 258 * @param name is the name of the variable 259 * @param scopeName is the optional scope name such as 'parent'. For servlet environments 260 * this could be 'application', 'session' or 'request'. 261 * @param value is the value of the attribute 262 */ 263 public void setVariable(String name, String scopeName, Object value) { 264 JellyContext scope = getScope(scopeName); 265 if ( scope != null ) { 266 scope.setVariable(name, value); 267 } 268 } 269 270 /*** Removes the given variable */ 271 public void removeVariable(String name) { 272 variables.remove(name); 273 } 274 275 /*** 276 * Removes the given variable in the specified scope. 277 * 278 * @param name is the name of the variable 279 * @param scopeName is the optional scope name such as 'parent'. For servlet environments 280 * this could be 'application', 'session' or 'request'. 281 * @param value is the value of the attribute 282 */ 283 public void removeVariable(String name, String scopeName) { 284 JellyContext scope = getScope(scopeName); 285 if ( scope != null ) { 286 scope.removeVariable(name); 287 } 288 } 289 290 /*** 291 * @return an Iterator over the current variable names in this 292 * context 293 */ 294 public Iterator getVariableNames() { 295 return variables.keySet().iterator(); 296 } 297 298 /*** 299 * @return the Map of variables in this scope 300 */ 301 public Map getVariables() { 302 return variables; 303 } 304 305 /*** 306 * Sets the Map of variables to use 307 */ 308 309 public void setVariables(Map variables) { 310 this.variables = variables; 311 } 312 313 /*** 314 * A factory method to create a new child context of the 315 * current context. 316 */ 317 public JellyContext newJellyContext(Map newVariables) { 318 // XXXX: should allow this new context to 319 // XXXX: inherit parent contexts? 320 // XXXX: Or at least publish the parent scope 321 // XXXX: as a Map in this new variable scope? 322 newVariables.put("parentScope", variables); 323 JellyContext answer = createChildContext(); 324 answer.setVariables(newVariables); 325 return answer; 326 } 327 328 /*** Registers the given tag library against the given namespace URI. 329 * This should be called before the parser is used. 330 */ 331 public void registerTagLibrary(String namespaceURI, TagLibrary taglib) { 332 if (log.isDebugEnabled()) { 333 log.debug("Registering tag library to: " + namespaceURI + " taglib: " + taglib); 334 } 335 taglibs.put(namespaceURI, taglib); 336 337 if (isExportLibraries() && parent != null) { 338 parent.registerTagLibrary( namespaceURI, taglib ); 339 } 340 341 if (isExportLibraries() && parent != null) { 342 parent.registerTagLibrary( namespaceURI, taglib ); 343 } 344 } 345 346 /*** Registers the given tag library class name against the given namespace URI. 347 * The class will be loaded via the given ClassLoader 348 * This should be called before the parser is used. 349 */ 350 public void registerTagLibrary( 351 String namespaceURI, 352 String className) { 353 354 if (log.isDebugEnabled()) { 355 log.debug("Registering tag library to: " + namespaceURI + " taglib: " + className); 356 } 357 taglibs.put(namespaceURI, className); 358 359 if (isExportLibraries() && parent != null) { 360 parent.registerTagLibrary( namespaceURI, className ); 361 } 362 } 363 364 public boolean isTagLibraryRegistered(String namespaceURI) { 365 boolean answer = taglibs.containsKey( namespaceURI ); 366 if (answer) { 367 return true; 368 } 369 else if ( parent != null ) { 370 return parent.isTagLibraryRegistered(namespaceURI); 371 } 372 else { 373 return false; 374 } 375 } 376 377 /*** 378 * @return the TagLibrary for the given namespace URI or null if one could not be found 379 */ 380 public TagLibrary getTagLibrary(String namespaceURI) { 381 382 // use my own mapping first, so that namespaceURIs can 383 // be redefined inside child contexts... 384 385 Object answer = taglibs.get(namespaceURI); 386 387 if ( answer == null && parent != null ) { 388 answer = parent.getTagLibrary( namespaceURI ); 389 } 390 391 if ( answer instanceof TagLibrary ) { 392 return (TagLibrary) answer; 393 } 394 else if ( answer instanceof String ) { 395 String className = (String) answer; 396 Class theClass = null; 397 try { 398 theClass = getClassLoader().loadClass(className); 399 } 400 catch (ClassNotFoundException e) { 401 log.error("Could not find the class: " + className, e); 402 } 403 if ( theClass != null ) { 404 try { 405 Object object = theClass.newInstance(); 406 if (object instanceof TagLibrary) { 407 taglibs.put(namespaceURI, object); 408 return (TagLibrary) object; 409 } 410 else { 411 log.error( 412 "The tag library object mapped to: " 413 + namespaceURI 414 + " is not a TagLibrary. Object = " 415 + object); 416 } 417 } 418 catch (Exception e) { 419 log.error( 420 "Could not instantiate instance of class: " + className + ". Reason: " + e, 421 e); 422 } 423 } 424 } 425 426 return null; 427 } 428 429 /*** 430 * Attempts to parse the script from the given uri using the 431 * {@link #getResource} method then returns the compiled script. 432 */ 433 public Script compileScript(String uri) throws Exception { 434 XMLParser parser = new XMLParser(); 435 parser.setContext(this); 436 InputStream in = getResourceAsStream(uri); 437 if (in == null) { 438 throw new JellyException("Could not find Jelly script: " + uri); 439 } 440 Script script = parser.parse(in); 441 return script.compile(); 442 } 443 444 /*** 445 * Attempts to parse the script from the given URL using the 446 * {@link #getResource} method then returns the compiled script. 447 */ 448 public Script compileScript(URL url) throws Exception { 449 XMLParser parser = new XMLParser(); 450 parser.setContext(this); 451 Script script = parser.parse(url.toString()); 452 return script.compile(); 453 } 454 455 /*** 456 * Parses the script from the given File then compiles it and runs it. 457 * 458 * @return the new child context that was used to run the script 459 */ 460 public JellyContext runScript(File file, XMLOutput output) throws Exception { 461 return runScript(file.toURL(), output); 462 } 463 464 /*** 465 * Parses the script from the given URL then compiles it and runs it. 466 * 467 * @return the new child context that was used to run the script 468 */ 469 public JellyContext runScript(URL url, XMLOutput output) throws Exception { 470 Script script = compileScript(url); 471 472 URL newJellyContextURL = getJellyContextURL(url); 473 JellyContext newJellyContext = new JellyContext(this, newJellyContextURL); 474 script.run(newJellyContext, output); 475 return newJellyContext; 476 } 477 478 /*** 479 * Parses the script from the given uri using the 480 * JellyContext.getResource() API then compiles it and runs it. 481 * 482 * @return the new child context that was used to run the script 483 */ 484 public JellyContext runScript(String uri, XMLOutput output) throws Exception { 485 URL url = getResource(uri); 486 if (url == null) { 487 throw new JellyException("Could not find Jelly script: " + url); 488 } 489 Script script = compileScript(url); 490 491 URL newJellyContextURL = getJellyContextURL(url); 492 JellyContext newJellyContext = new JellyContext(this, newJellyContextURL); 493 script.run(newJellyContext, output); 494 return newJellyContext; 495 } 496 497 /*** 498 * Parses the script from the given uri using the 499 * JellyContext.getResource() API then compiles it and runs it. 500 * 501 * @return the new child context that was used to run the script 502 */ 503 public JellyContext runScript(String uri, XMLOutput output, 504 boolean export, boolean inherit) throws Exception { 505 URL url = getResource(uri); 506 if (url == null) { 507 throw new JellyException("Could not find Jelly script: " + url); 508 } 509 Script script = compileScript(url); 510 511 URL newJellyContextURL = getJellyContextURL(url); 512 513 JellyContext newJellyContext = new JellyContext(this, newJellyContextURL); 514 newJellyContext.setExport( export ); 515 newJellyContext.setInherit( inherit ); 516 517 if ( inherit ) { 518 // use the same variable scopes 519 newJellyContext.variables = this.variables; 520 } 521 522 script.run(newJellyContext, output); 523 524 return newJellyContext; 525 } 526 527 /*** 528 * Returns a URL for the given resource from the specified path. 529 * If the uri starts with "/" then the path is taken as relative to 530 * the current context root. If the uri is a well formed URL then it 531 * is used. Otherwise the uri is interpreted as relative to the current 532 * context (the location of the current script). 533 */ 534 public URL getResource(String uri) throws MalformedURLException { 535 if (uri.startsWith("/")) { 536 // append this uri to the context root 537 return createRelativeURL(rootURL, uri.substring(1)); 538 } 539 else { 540 try { 541 return new URL(uri); 542 } 543 catch (MalformedURLException e) { 544 // lets try find a relative resource 545 try { 546 return createRelativeURL(currentURL, uri); 547 } 548 catch (MalformedURLException e2) { 549 throw e; 550 } 551 } 552 } 553 } 554 555 /*** 556 * Attempts to open an InputStream to the given resource at the specified path. 557 * If the uri starts with "/" then the path is taken as relative to 558 * the current context root. If the uri is a well formed URL then it 559 * is used. Otherwise the uri is interpreted as relative to the current 560 * context (the location of the current script). 561 * 562 * @return null if this resource could not be loaded, otherwise the resources 563 * input stream is returned. 564 */ 565 public InputStream getResourceAsStream(String uri) { 566 try { 567 URL url = getResource(uri); 568 return url.openStream(); 569 } 570 catch (Exception e) { 571 if (log.isTraceEnabled()) { 572 log.trace( 573 "Caught exception attempting to open: " + uri + ". Exception: " + e, 574 e); 575 } 576 return null; 577 } 578 } 579 580 581 // Properties 582 //------------------------------------------------------------------------- 583 584 /*** 585 * @return the current root context URL from which all absolute resource URIs 586 * will be relative to. For example in a web application the root URL will 587 * map to the web directory which contains the WEB-INF directory. 588 */ 589 public URL getRootURL() { 590 return rootURL; 591 } 592 593 /*** 594 * Sets the current root context URL from which all absolute resource URIs 595 * will be relative to. For example in a web application the root URL will 596 * map to the web directory which contains the WEB-INF directory. 597 */ 598 public void setRootURL(URL rootURL) { 599 this.rootURL = rootURL; 600 } 601 602 603 /*** 604 * @return the current URL context of the current script that is executing. 605 * This URL context is used to deduce relative scripts when relative URIs are 606 * used in calls to {@link #getResource} to process relative scripts. 607 */ 608 public URL getCurrentURL() { 609 return currentURL; 610 } 611 612 /*** 613 * Sets the current URL context of the current script that is executing. 614 * This URL context is used to deduce relative scripts when relative URIs are 615 * used in calls to {@link #getResource} to process relative scripts. 616 */ 617 public void setCurrentURL(URL currentURL) { 618 this.currentURL = currentURL; 619 } 620 621 /*** 622 * Returns whether caching of Tag instances, per thread, is enabled. 623 * Caching Tags can boost performance, on some JVMs, by reducing the cost of 624 * object construction when running Jelly inside a multi-threaded application server 625 * such as a Servlet engine. 626 * 627 * @return whether caching of Tag instances is enabled. 628 */ 629 public boolean isCacheTags() { 630 return cacheTags; 631 } 632 633 /*** 634 * Sets whether caching of Tag instances, per thread, is enabled. 635 * Caching Tags can boost performance, on some JVMs, by reducing the cost of 636 * object construction when running Jelly inside a multi-threaded application server 637 * such as a Servlet engine. 638 * 639 * @param cacheTags Whether caching should be enabled or disabled. 640 */ 641 public void setCacheTags(boolean cacheTags) { 642 this.cacheTags = cacheTags; 643 } 644 645 /*** 646 * Returns whether we export tag libraries to our parents context 647 * @return boolean 648 */ 649 public boolean isExportLibraries() { 650 return exportLibraries; 651 } 652 653 /*** 654 * Sets whether we export tag libraries to our parents context 655 * @param exportLibraries The exportLibraries to set 656 */ 657 public void setExportLibraries(boolean exportLibraries) { 658 this.exportLibraries = exportLibraries; 659 } 660 661 662 /*** 663 * Sets whether we should export variable definitions to our parent context 664 */ 665 public void setExport(boolean export) { 666 this.export = export; 667 } 668 669 public boolean isExport() { 670 return this.export; 671 } 672 673 /*** 674 * Sets whether we should inherit variables from our parent context 675 */ 676 public void setInherit(boolean inherit) { 677 this.inherit = inherit; 678 } 679 680 public boolean isInherit() { 681 return this.inherit; 682 } 683 684 685 /*** 686 * Return the class loader to be used for instantiating application objects 687 * when required. This is determined based upon the following rules: 688 * <ul> 689 * <li>The class loader set by <code>setClassLoader()</code>, if any</li> 690 * <li>The thread context class loader, if it exists and the 691 * <code>useContextClassLoader</code> property is set to true</li> 692 * <li>The class loader used to load the XMLParser class itself. 693 * </ul> 694 */ 695 public ClassLoader getClassLoader() { 696 if (this.classLoader != null) { 697 return (this.classLoader); 698 } 699 if (this.useContextClassLoader) { 700 ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); 701 if (classLoader != null) { 702 return (classLoader); 703 } 704 } 705 return (this.getClass().getClassLoader()); 706 } 707 708 /*** 709 * Set the class loader to be used for instantiating application objects 710 * when required. 711 * 712 * @param classLoader The new class loader to use, or <code>null</code> 713 * to revert to the standard rules 714 */ 715 public void setClassLoader(ClassLoader classLoader) { 716 this.classLoader = classLoader; 717 } 718 719 /*** 720 * Return the boolean as to whether the context classloader should be used. 721 */ 722 public boolean getUseContextClassLoader() { 723 return useContextClassLoader; 724 } 725 726 /*** 727 * Determine whether to use the Context ClassLoader (the one found by 728 * calling <code>Thread.currentThread().getContextClassLoader()</code>) 729 * to resolve/load classes. If not 730 * using Context ClassLoader, then the class-loading defaults to 731 * using the calling-class' ClassLoader. 732 * 733 * @param boolean determines whether to use JellyContext ClassLoader. 734 */ 735 public void setUseContextClassLoader(boolean use) { 736 useContextClassLoader = use; 737 } 738 739 740 // Implementation methods 741 //------------------------------------------------------------------------- 742 /*** 743 * @return a new relative URL from the given root and with the addition of the 744 * extra relative URI 745 * 746 * @param rootURL is the root context from which the relative URI will be applied 747 * @param relativeURI is the relative URI (without a leading "/") 748 * @throws MalformedURLException if the URL is invalid. 749 */ 750 protected URL createRelativeURL(URL rootURL, String relativeURI) 751 throws MalformedURLException { 752 String urlText = null; 753 if (rootURL == null) { 754 String userDir = System.getProperty("user.dir"); 755 urlText = "file://" + userDir + relativeURI; 756 } 757 else { 758 urlText = rootURL.toString() + relativeURI; 759 } 760 if ( log.isDebugEnabled() ) { 761 log.debug("Attempting to open url: " + urlText); 762 } 763 return new URL(urlText); 764 } 765 766 /*** 767 * Strips off the name of a script to create a new context URL 768 */ 769 protected URL getJellyContextURL(URL url) throws MalformedURLException { 770 String text = url.toString(); 771 int idx = text.lastIndexOf('/'); 772 text = text.substring(0, idx + 1); 773 return new URL(text); 774 } 775 776 /*** 777 * Factory method to create a new child of this context 778 */ 779 protected JellyContext createChildContext() { 780 return new JellyContext(this); 781 } 782 783 }

This page was automatically generated by Maven