1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.apache.commons.jelly;
17
18 import java.io.File;
19 import java.io.InputStream;
20 import java.io.IOException;
21 import java.net.MalformedURLException;
22 import java.net.URL;
23 import java.util.Hashtable;
24 import java.util.Iterator;
25 import java.util.Map;
26
27 import org.apache.commons.jelly.parser.XMLParser;
28 import org.apache.commons.jelly.util.ClassLoaderUtils;
29 import org.apache.commons.logging.Log;
30 import org.apache.commons.logging.LogFactory;
31
32 import org.xml.sax.InputSource;
33 import org.xml.sax.SAXException;
34
35 /***
36 * <p><code>JellyContext</code> represents the Jelly context.</p>
37 *
38 * @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
39 * @version $Revision: 1.10 $
40 */
41 public class JellyContext {
42
43 /*** The Log to which logging calls will be made. */
44 private static final Log log = LogFactory.getLog(JellyContext.class);
45
46 /*** Default for inheritance of variables **/
47 private static final boolean DEFAULT_INHERIT = true;
48
49 /*** Default for export of variables **/
50 private static final boolean DEFAULT_EXPORT = false;
51
52 /*** String used to denote a script can't be parsed */
53 private static final String BAD_PARSE = "Could not parse Jelly script";
54
55 /***
56 * The class loader to use for instantiating application objects.
57 * If not specified, the context class loader, or the class loader
58 * used to load this class itself, is used, based on the value of the
59 * <code>useContextClassLoader</code> variable.
60 */
61 protected ClassLoader classLoader;
62
63 /***
64 * Do we want to use the Context ClassLoader when loading classes
65 * for instantiating new objects? Default is <code>false</code>.
66 */
67 protected boolean useContextClassLoader = false;
68
69 /*** The root URL context (where scripts are located from) */
70 private URL rootURL;
71
72 /*** The current URL context (where relative scripts are located from) */
73 private URL currentURL;
74
75 /*** Tag libraries found so far */
76 private Map taglibs = new Hashtable();
77
78 /*** synchronized access to the variables in scope */
79 private Map variables = new Hashtable();
80
81 /*** The parent context */
82 private JellyContext parent;
83
84 /*** Do we inherit variables from parent context? */
85 private boolean inherit = JellyContext.DEFAULT_INHERIT;
86
87 /*** Do we export our variables to parent context? */
88 private boolean export = JellyContext.DEFAULT_EXPORT;
89
90 /*** Should we export tag libraries to our parents context */
91 private boolean exportLibraries = true;
92
93 /*** Should we cache Tag instances, per thread, to reduce object contruction overhead? */
94 private boolean cacheTags = false;
95
96 /***
97 * Create a new context with the currentURL set to the rootURL
98 */
99 public JellyContext() {
100 this.currentURL = rootURL;
101 init();
102 }
103
104 /***
105 * Create a new context with the given rootURL
106 * @param rootURL the root URL used in resolving absolute resources i.e. those starting with '/'
107 */
108 public JellyContext(URL rootURL) {
109 this( rootURL, rootURL );
110 }
111
112 /***
113 * Create a new context with the given rootURL and currentURL
114 * @param rootURL the root URL used in resolving absolute resources i.e. those starting with '/'
115 * @param currentURL the root URL used in resolving relative resources
116 */
117 public JellyContext(URL rootURL, URL currentURL) {
118 this.rootURL = rootURL;
119 this.currentURL = currentURL;
120 init();
121 }
122
123
124 /***
125 * Create a new context with the given parent context.
126 * The parent's rootURL and currentURL are set on the child, and the parent's variables are
127 * available in the child context under the name <code>parentScope</code>.
128 *
129 * @param parent the parent context for the newly created context.
130 */
131 public JellyContext(JellyContext parent) {
132 this.parent = parent;
133 this.rootURL = parent.rootURL;
134 this.currentURL = parent.currentURL;
135 this.variables.put("parentScope", parent.variables);
136 this.cacheTags = parent.cacheTags;
137 init();
138 }
139
140 /***
141 * Create a new context with the given parent context.
142 * The parent's rootURL are set on the child, and the parent's variables are
143 * available in the child context under the name <code>parentScope</code>.
144 *
145 * @param parentJellyContext the parent context for the newly created context.
146 * @param currentURL the root URL used in resolving relative resources
147 */
148 public JellyContext(JellyContext parentJellyContext, URL currentURL) {
149 this(parentJellyContext);
150 this.currentURL = currentURL;
151 }
152
153 /***
154 * Create a new context with the given parent context.
155 * The parent's variables are available in the child context under the name <code>parentScope</code>.
156 *
157 * @param parentJellyContext the parent context for the newly created context.
158 * @param rootURL the root URL used in resolving absolute resources i.e. those starting with '/'
159 * @param currentURL the root URL used in resolving relative resources
160 */
161 public JellyContext(JellyContext parentJellyContext, URL rootURL, URL currentURL) {
162 this(parentJellyContext, currentURL);
163 this.rootURL = rootURL;
164 }
165
166 /***
167 * Initialize the context.
168 * This includes adding the context to itself under the name <code>context</code> and
169 * making the System Properties available as <code>systemScope</code>
170 */
171 private void init() {
172 variables.put("context",this);
173 try {
174 variables.put("systemScope", System.getProperties() );
175 } catch (SecurityException e) {
176 log.debug("security exception accessing system properties", e);
177 }
178 }
179
180 /***
181 * @return the parent context for this context
182 */
183 public JellyContext getParent() {
184 return parent;
185 }
186
187 /***
188 * @return the scope of the given name, such as the 'parent' scope.
189 * If Jelly is used in a Servlet situation then 'request', 'session' and 'application' are other names
190 * for scopes
191 */
192 public JellyContext getScope(String name) {
193 if ( "parent".equals( name ) ) {
194 return getParent();
195 }
196 return null;
197 }
198
199 /***
200 * Finds the variable value of the given name in this context or in any other parent context.
201 * If this context does not contain the variable, then its parent is used and then its parent
202 * and so forth until the context with no parent is found.
203 *
204 * @return the value of the variable in this or one of its descendant contexts or null
205 * if the variable could not be found.
206 */
207 public Object findVariable(String name) {
208 Object answer = variables.get(name);
209 boolean definedHere = answer != null || variables.containsKey(name);
210
211 if (definedHere) return answer;
212
213 if ( answer == null && parent != null ) {
214 answer = parent.findVariable(name);
215 }
216
217 if ( answer == null ) {
218 answer = getSystemProperty(name);
219 }
220
221 if (log.isDebugEnabled()) {
222 log.debug("findVariable: " + name + " value: " + answer );
223 }
224 return answer;
225 }
226
227
228 /*** @return the value of the given variable name */
229 public Object getVariable(String name) {
230 Object value = variables.get(name);
231 boolean definedHere = value != null || variables.containsKey(name);
232
233 if (definedHere) return value;
234
235 if ( value == null && isInherit() ) {
236 JellyContext parentContext = getParent();
237 if (parentContext != null) {
238 value = parentContext.getVariable( name );
239 }
240 }
241
242
243 if ( value == null ) {
244 value = getSystemProperty(name);
245 }
246
247 return value;
248 }
249
250 /***
251 * Get a system property and handle security exceptions
252 * @param name the name of the property to retrieve
253 * @return the value of the property, or null if a SecurityException occurs
254 */
255 private Object getSystemProperty(String name) {
256 try {
257 return System.getProperty(name);
258 }
259 catch (SecurityException e) {
260 log.debug("security exception accessing system properties", e);
261 }
262 return null;
263 }
264
265 /***
266 * @return the value of the given variable name in the given variable scope
267 * @param name is the name of the variable
268 * @param scopeName is the optional scope name such as 'parent'. For servlet environments
269 * this could be 'application', 'session' or 'request'.
270 */
271 public Object getVariable(String name, String scopeName) {
272 JellyContext scope = getScope(scopeName);
273 if ( scope != null ) {
274 return scope.getVariable(name);
275 }
276 return null;
277 }
278
279
280
281 /*** Sets the value of the named variable */
282 public void setVariable(String name, Object value) {
283 if ( isExport() ) {
284 getParent().setVariable( name, value );
285 return;
286 }
287 if (value == null) {
288 variables.remove(name);
289 }
290 else {
291 variables.put(name, value);
292 }
293 }
294
295 /***
296 * Sets the value of the given variable name in the given variable scope
297 * @param name is the name of the variable
298 * @param scopeName is the optional scope name such as 'parent'. For servlet environments
299 * this could be 'application', 'session' or 'request'.
300 * @param value is the value of the attribute
301 */
302 public void setVariable(String name, String scopeName, Object value) {
303 JellyContext scope = getScope(scopeName);
304 if ( scope != null ) {
305 scope.setVariable(name, value);
306 }
307 }
308
309 /*** Removes the given variable */
310 public void removeVariable(String name) {
311 variables.remove(name);
312 }
313
314 /***
315 * Removes the given variable in the specified scope.
316 *
317 * @param name is the name of the variable
318 * @param scopeName is the optional scope name such as 'parent'. For servlet environments
319 * this could be 'application', 'session' or 'request'.
320 */
321 public void removeVariable(String name, String scopeName) {
322 JellyContext scope = getScope(scopeName);
323 if ( scope != null ) {
324 scope.removeVariable(name);
325 }
326 }
327
328 /***
329 * @return an Iterator over the current variable names in this
330 * context
331 */
332 public Iterator getVariableNames() {
333 return variables.keySet().iterator();
334 }
335
336 /***
337 * @return the Map of variables in this scope
338 */
339 public Map getVariables() {
340 return variables;
341 }
342
343 /***
344 * Sets the Map of variables to use
345 */
346 public void setVariables(Map variables) {
347
348
349 for (Iterator iter = variables.entrySet().iterator(); iter.hasNext();) {
350 Map.Entry element = (Map.Entry) iter.next();
351 if (element.getValue() != null) {
352 this.variables.put(element.getKey(), element.getValue());
353 }
354 }
355
356 }
357
358 /***
359 * A factory method to create a new child context of the
360 * current context.
361 */
362 public JellyContext newJellyContext(Map newVariables) {
363
364
365
366
367 newVariables.put("parentScope", variables);
368 JellyContext answer = createChildContext();
369 answer.setVariables(newVariables);
370 return answer;
371 }
372
373 /***
374 * A factory method to create a new child context of the
375 * current context.
376 */
377 public JellyContext newJellyContext() {
378 return createChildContext();
379 }
380
381 /*** Registers the given tag library against the given namespace URI.
382 * This should be called before the parser is used.
383 */
384 public void registerTagLibrary(String namespaceURI, TagLibrary taglib) {
385 if (log.isDebugEnabled()) {
386 log.debug("Registering tag library to: " + namespaceURI + " taglib: " + taglib);
387 }
388 taglibs.put(namespaceURI, taglib);
389
390 if (isExportLibraries() && parent != null) {
391 parent.registerTagLibrary( namespaceURI, taglib );
392 }
393 }
394
395 /*** Registers the given tag library class name against the given namespace URI.
396 * The class will be loaded via the given ClassLoader
397 * This should be called before the parser is used.
398 */
399 public void registerTagLibrary(
400 String namespaceURI,
401 String className) {
402
403 if (log.isDebugEnabled()) {
404 log.debug("Registering tag library to: " + namespaceURI + " taglib: " + className);
405 }
406 taglibs.put(namespaceURI, className);
407
408 if (isExportLibraries() && parent != null) {
409 parent.registerTagLibrary( namespaceURI, className );
410 }
411 }
412
413 public boolean isTagLibraryRegistered(String namespaceURI) {
414 boolean answer = taglibs.containsKey( namespaceURI );
415 if (answer) {
416 return true;
417 }
418 else if ( parent != null ) {
419 return parent.isTagLibraryRegistered(namespaceURI);
420 }
421 else {
422 return false;
423 }
424 }
425
426 /***
427 * @return the TagLibrary for the given namespace URI or null if one could not be found
428 */
429 public TagLibrary getTagLibrary(String namespaceURI) {
430
431
432
433
434 Object answer = taglibs.get(namespaceURI);
435
436 if ( answer == null && parent != null ) {
437 answer = parent.getTagLibrary( namespaceURI );
438 }
439
440 if ( answer instanceof TagLibrary ) {
441 return (TagLibrary) answer;
442 }
443 else if ( answer instanceof String ) {
444 String className = (String) answer;
445 Class theClass = null;
446 try {
447 theClass = getClassLoader().loadClass(className);
448 }
449 catch (ClassNotFoundException e) {
450 log.error("Could not find the class: " + className, e);
451 }
452 if ( theClass != null ) {
453 try {
454 Object object = theClass.newInstance();
455 if (object instanceof TagLibrary) {
456 taglibs.put(namespaceURI, object);
457 return (TagLibrary) object;
458 }
459 else {
460 log.error(
461 "The tag library object mapped to: "
462 + namespaceURI
463 + " is not a TagLibrary. Object = "
464 + object);
465 }
466 }
467 catch (Exception e) {
468 log.error(
469 "Could not instantiate instance of class: " + className + ". Reason: " + e,
470 e);
471 }
472 }
473 }
474
475 return null;
476 }
477
478 /***
479 * Attempts to parse the script from the given uri using the
480 * {@link #getResource} method then returns the compiled script.
481 */
482 public Script compileScript(String uri) throws JellyException {
483 XMLParser parser = getXMLParser();
484 parser.setContext(this);
485 InputStream in = getResourceAsStream(uri);
486 if (in == null) {
487 throw new JellyException("Could not find Jelly script: " + uri);
488 }
489 Script script = null;
490 try {
491 script = parser.parse(in);
492 } catch (IOException e) {
493 throw new JellyException(JellyContext.BAD_PARSE, e);
494 } catch (SAXException e) {
495 throw new JellyException(JellyContext.BAD_PARSE, e);
496 }
497
498 return script.compile();
499 }
500
501 /***
502 * Attempts to parse the script from the given URL using the
503 * {@link #getResource} method then returns the compiled script.
504 */
505 public Script compileScript(URL url) throws JellyException {
506 XMLParser parser = getXMLParser();
507 parser.setContext(this);
508
509 Script script = null;
510 try {
511 script = parser.parse(url.toString());
512 } catch (IOException e) {
513 throw new JellyException(JellyContext.BAD_PARSE, e);
514 } catch (SAXException e) {
515 throw new JellyException(JellyContext.BAD_PARSE, e);
516 }
517
518 return script.compile();
519 }
520
521 /***
522 * Attempts to parse the script from the given InputSource using the
523 * {@link #getResource} method then returns the compiled script.
524 */
525 public Script compileScript(InputSource source) throws JellyException {
526 XMLParser parser = getXMLParser();
527 parser.setContext(this);
528
529 Script script = null;
530 try {
531 script = parser.parse(source);
532 } catch (IOException e) {
533 throw new JellyException(JellyContext.BAD_PARSE, e);
534 } catch (SAXException e) {
535 throw new JellyException(JellyContext.BAD_PARSE, e);
536 }
537
538 return script.compile();
539 }
540
541 /***
542 * @return a thread pooled XMLParser to avoid the startup overhead
543 * of the XMLParser
544 */
545 protected XMLParser getXMLParser() {
546 XMLParser parser = createXMLParser();
547 return parser;
548 }
549
550 /***
551 * Factory method to allow JellyContext implementations to overload how an XMLParser
552 * is created - such as to overload what the default ExpressionFactory should be.
553 */
554 protected XMLParser createXMLParser() {
555 return new XMLParser();
556 }
557
558 /***
559 * Parses the script from the given File then compiles it and runs it.
560 *
561 * @return the new child context that was used to run the script
562 */
563 public JellyContext runScript(File file, XMLOutput output) throws JellyException {
564 try {
565 return runScript(file.toURL(), output, JellyContext.DEFAULT_EXPORT,
566 JellyContext.DEFAULT_INHERIT);
567 } catch (MalformedURLException e) {
568 throw new JellyException(e.toString());
569 }
570 }
571
572 /***
573 * Parses the script from the given URL then compiles it and runs it.
574 *
575 * @return the new child context that was used to run the script
576 */
577 public JellyContext runScript(URL url, XMLOutput output) throws JellyException {
578 return runScript(url, output, JellyContext.DEFAULT_EXPORT,
579 JellyContext.DEFAULT_INHERIT);
580 }
581
582 /***
583 * Parses the script from the given InputSource then compiles it and runs it.
584 *
585 * @return the new child context that was used to run the script
586 */
587 public JellyContext runScript(InputSource source, XMLOutput output) throws JellyException {
588 return runScript(source, output, JellyContext.DEFAULT_EXPORT,
589 JellyContext.DEFAULT_INHERIT);
590 }
591
592 /***
593 * Parses the script from the given uri using the
594 * JellyContext.getResource() API then compiles it and runs it.
595 *
596 * @return the new child context that was used to run the script
597 */
598 public JellyContext runScript(String uri, XMLOutput output) throws JellyException {
599 URL url = null;
600 try {
601 url = getResource(uri);
602 } catch (MalformedURLException e) {
603 throw new JellyException(e.toString());
604 }
605
606 if (url == null) {
607 throw new JellyException("Could not find Jelly script: " + url);
608 }
609 return runScript(url, output, JellyContext.DEFAULT_EXPORT,
610 JellyContext.DEFAULT_INHERIT);
611 }
612
613 /***
614 * Parses the script from the given uri using the
615 * JellyContext.getResource() API then compiles it and runs it.
616 *
617 * @return the new child context that was used to run the script
618 */
619 public JellyContext runScript(String uri, XMLOutput output,
620 boolean export, boolean inherit) throws JellyException {
621 URL url = null;
622 try {
623 url = getResource(uri);
624 } catch (MalformedURLException e) {
625 throw new JellyException(e.toString());
626 }
627
628 if (url == null) {
629 throw new JellyException("Could not find Jelly script: " + url);
630 }
631
632 return runScript(url, output, export, inherit);
633 }
634
635 /***
636 * Parses the script from the given file then compiles it and runs it.
637 *
638 * @return the new child context that was used to run the script
639 */
640 public JellyContext runScript(File file, XMLOutput output,
641 boolean export, boolean inherit) throws JellyException {
642 try {
643 return runScript(file.toURL(), output, export, inherit);
644 } catch (MalformedURLException e) {
645 throw new JellyException(e.toString());
646 }
647 }
648
649 /***
650 * Parses the script from the given URL then compiles it and runs it.
651 *
652 * @return the new child context that was used to run the script
653 */
654 public JellyContext runScript(URL url, XMLOutput output,
655 boolean export, boolean inherit) throws JellyException {
656 return runScript(new InputSource(url.toString()), output, export, inherit);
657 }
658
659 /***
660 * Parses the script from the given InputSource then compiles it and runs it.
661 *
662 * @return the new child context that was used to run the script
663 */
664 public JellyContext runScript(InputSource source, XMLOutput output,
665 boolean export, boolean inherit) throws JellyException {
666 Script script = compileScript(source);
667
668 URL newJellyContextURL = null;
669 try {
670 newJellyContextURL = getJellyContextURL(source);
671 } catch (MalformedURLException e) {
672 throw new JellyException(e.toString());
673 }
674
675 JellyContext newJellyContext = newJellyContext();
676 newJellyContext.setRootURL( newJellyContextURL );
677 newJellyContext.setCurrentURL( newJellyContextURL );
678 newJellyContext.setExport( export );
679 newJellyContext.setInherit( inherit );
680
681 if ( inherit ) {
682
683 newJellyContext.variables = this.variables;
684 }
685
686 if (log.isDebugEnabled() ) {
687 log.debug( "About to run script: " + source.getSystemId() );
688 log.debug( "root context URL: " + newJellyContext.rootURL );
689 log.debug( "current context URL: " + newJellyContext.currentURL );
690 }
691
692 script.run(newJellyContext, output);
693
694 return newJellyContext;
695 }
696
697 /***
698 * Returns a URL for the given resource from the specified path.
699 * If the uri starts with "/" then the path is taken as relative to
700 * the current context root.
701 * If the uri is a well formed URL then it is used.
702 * If the uri is a file that exists and can be read then it is used.
703 * Otherwise the uri is interpreted as relative to the current context (the
704 * location of the current script).
705 */
706 public URL getResource(String uri) throws MalformedURLException {
707 if (uri.startsWith("/")) {
708
709 return createRelativeURL(rootURL, uri.substring(1));
710 }
711 else {
712 try {
713 return new URL(uri);
714 }
715 catch (MalformedURLException e) {
716
717 try {
718 return createRelativeURL(currentURL, uri);
719 } catch (MalformedURLException e2) {
720 throw e;
721 }
722 }
723 }
724 }
725
726 /***
727 * Attempts to open an InputStream to the given resource at the specified path.
728 * If the uri starts with "/" then the path is taken as relative to
729 * the current context root. If the uri is a well formed URL then it
730 * is used. Otherwise the uri is interpreted as relative to the current
731 * context (the location of the current script).
732 *
733 * @return null if this resource could not be loaded, otherwise the resources
734 * input stream is returned.
735 */
736 public InputStream getResourceAsStream(String uri) {
737 try {
738 URL url = getResource(uri);
739 return url.openStream();
740 }
741 catch (Exception e) {
742 if (log.isTraceEnabled()) {
743 log.trace(
744 "Caught exception attempting to open: " + uri + ". Exception: " + e,
745 e);
746 }
747 return null;
748 }
749 }
750
751
752
753
754
755 /***
756 * @return the current root context URL from which all absolute resource URIs
757 * will be relative to. For example in a web application the root URL will
758 * map to the web directory which contains the WEB-INF directory.
759 */
760 public URL getRootURL() {
761 return rootURL;
762 }
763
764 /***
765 * Sets the current root context URL from which all absolute resource URIs
766 * will be relative to. For example in a web application the root URL will
767 * map to the web directory which contains the WEB-INF directory.
768 */
769 public void setRootURL(URL rootURL) {
770 this.rootURL = rootURL;
771 }
772
773
774 /***
775 * @return the current URL context of the current script that is executing.
776 * This URL context is used to deduce relative scripts when relative URIs are
777 * used in calls to {@link #getResource} to process relative scripts.
778 */
779 public URL getCurrentURL() {
780 return currentURL;
781 }
782
783 /***
784 * Sets the current URL context of the current script that is executing.
785 * This URL context is used to deduce relative scripts when relative URIs are
786 * used in calls to {@link #getResource} to process relative scripts.
787 */
788 public void setCurrentURL(URL currentURL) {
789 this.currentURL = currentURL;
790 }
791
792 /***
793 * Returns whether caching of Tag instances, per thread, is enabled.
794 * Caching Tags can boost performance, on some JVMs, by reducing the cost of
795 * object construction when running Jelly inside a multi-threaded application server
796 * such as a Servlet engine.
797 *
798 * @return whether caching of Tag instances is enabled.
799 */
800 public boolean isCacheTags() {
801 return cacheTags;
802 }
803
804 /***
805 * Sets whether caching of Tag instances, per thread, is enabled.
806 * Caching Tags can boost performance, on some JVMs, by reducing the cost of
807 * object construction when running Jelly inside a multi-threaded application server
808 * such as a Servlet engine.
809 *
810 * @param cacheTags Whether caching should be enabled or disabled.
811 */
812 public void setCacheTags(boolean cacheTags) {
813 this.cacheTags = cacheTags;
814 }
815
816 /***
817 * Returns whether we export tag libraries to our parents context
818 * @return boolean
819 */
820 public boolean isExportLibraries() {
821 return exportLibraries;
822 }
823
824 /***
825 * Sets whether we export tag libraries to our parents context
826 * @param exportLibraries The exportLibraries to set
827 */
828 public void setExportLibraries(boolean exportLibraries) {
829 this.exportLibraries = exportLibraries;
830 }
831
832
833 /***
834 * Sets whether we should export variable definitions to our parent context
835 */
836 public void setExport(boolean export) {
837 this.export = export;
838 }
839
840 /***
841 * @return whether we should export variable definitions to our parent context
842 */
843 public boolean isExport() {
844 return this.export;
845 }
846
847 /***
848 * Sets whether we should inherit variables from our parent context
849 */
850 public void setInherit(boolean inherit) {
851 this.inherit = inherit;
852 }
853
854 /***
855 * @return whether we should inherit variables from our parent context
856 */
857 public boolean isInherit() {
858 return this.inherit;
859 }
860
861
862 /***
863 * Return the class loader to be used for instantiating application objects
864 * when required. This is determined based upon the following rules:
865 * <ul>
866 * <li>The class loader set by <code>setClassLoader()</code>, if any</li>
867 * <li>The thread context class loader, if it exists and the
868 * <code>useContextClassLoader</code> property is set to true</li>
869 * <li>The class loader used to load the XMLParser class itself.
870 * </ul>
871 */
872 public ClassLoader getClassLoader() {
873 return ClassLoaderUtils.getClassLoader(classLoader, useContextClassLoader, getClass());
874 }
875
876 /***
877 * Set the class loader to be used for instantiating application objects
878 * when required.
879 *
880 * @param classLoader The new class loader to use, or <code>null</code>
881 * to revert to the standard rules
882 */
883 public void setClassLoader(ClassLoader classLoader) {
884 this.classLoader = classLoader;
885 }
886
887 /***
888 * Return the boolean as to whether the context classloader should be used.
889 */
890 public boolean getUseContextClassLoader() {
891 return useContextClassLoader;
892 }
893
894 /***
895 * Determine whether to use the Context ClassLoader (the one found by
896 * calling <code>Thread.currentThread().getContextClassLoader()</code>)
897 * to resolve/load classes. If not
898 * using Context ClassLoader, then the class-loading defaults to
899 * using the calling-class' ClassLoader.
900 *
901 * @param use determines whether to use JellyContext ClassLoader.
902 */
903 public void setUseContextClassLoader(boolean use) {
904 useContextClassLoader = use;
905 }
906
907
908
909
910 /***
911 * @return a new relative URL from the given root and with the addition of the
912 * extra relative URI
913 *
914 * @param rootURL is the root context from which the relative URI will be applied
915 * @param relativeURI is the relative URI (without a leading "/")
916 * @throws MalformedURLException if the URL is invalid.
917 */
918 protected URL createRelativeURL(URL rootURL, String relativeURI)
919 throws MalformedURLException {
920 URL url = rootURL;
921 if (url == null) {
922 File file = new File(System.getProperty("user.dir"));
923 url = file.toURL();
924 }
925 String urlText = url.toString() + relativeURI;
926 if ( log.isDebugEnabled() ) {
927 log.debug("Attempting to open url: " + urlText);
928 }
929 return new URL(urlText);
930 }
931
932 /***
933 * Strips off the name of a script to create a new context URL
934 */
935 protected URL getJellyContextURL(URL url) throws MalformedURLException {
936 String text = url.toString();
937 int idx = text.lastIndexOf('/');
938 text = text.substring(0, idx + 1);
939 return new URL(text);
940 }
941
942 /***
943 * Strips off the name of a script to create a new context URL
944 */
945 protected URL getJellyContextURL(InputSource source) throws MalformedURLException {
946 String text = source.getSystemId();
947 int idx = text.lastIndexOf('/');
948 text = text.substring(0, idx + 1);
949 return new URL(text);
950 }
951
952 /***
953 * Factory method to create a new child of this context
954 */
955 protected JellyContext createChildContext() {
956 return new JellyContext(this);
957 }
958
959 /***
960 * Change the parent context to the one provided
961 * @param context the new parent context
962 */
963 protected void setParent(JellyContext context)
964 {
965 parent = context;
966 this.variables.put("parentScope", parent.variables);
967
968 if (isExportLibraries() && parent != null) {
969 for (Iterator keys = taglibs.keySet().iterator(); keys.hasNext();)
970 {
971 String namespaceURI = (String) keys.next();
972 Object tagLibOrClassName = taglibs.get(namespaceURI);
973 if (tagLibOrClassName instanceof TagLibrary)
974 {
975 parent.registerTagLibrary( namespaceURI, (TagLibrary) tagLibOrClassName );
976 }
977 else
978 {
979 parent.registerTagLibrary( namespaceURI, (String) tagLibOrClassName );
980 }
981 }
982 }
983
984 }
985
986 }