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.IOException;
20 import java.io.InputStream;
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: 227285 $
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 /*** Clears variables set by Tags.
382 * @see #clearVariables()
383 */
384 public void clear() {
385 clearVariables();
386 }
387
388 /*** Clears variables set by Tags (variables set while running a Jelly script)
389 * @see #clear()
390 */
391 protected void clearVariables() {
392 variables.clear();
393 }
394
395 /*** Registers the given tag library against the given namespace URI.
396 * This should be called before the parser is used.
397 */
398 public void registerTagLibrary(String namespaceURI, TagLibrary taglib) {
399 if (log.isDebugEnabled()) {
400 log.debug("Registering tag library to: " + namespaceURI + " taglib: " + taglib);
401 }
402 taglibs.put(namespaceURI, taglib);
403
404 if (isExportLibraries() && parent != null) {
405 parent.registerTagLibrary( namespaceURI, taglib );
406 }
407 }
408
409 /*** Registers the given tag library class name against the given namespace URI.
410 * The class will be loaded via the given ClassLoader
411 * This should be called before the parser is used.
412 */
413 public void registerTagLibrary(
414 String namespaceURI,
415 String className) {
416
417 if (log.isDebugEnabled()) {
418 log.debug("Registering tag library to: " + namespaceURI + " taglib: " + className);
419 }
420 taglibs.put(namespaceURI, className);
421
422 if (isExportLibraries() && parent != null) {
423 parent.registerTagLibrary( namespaceURI, className );
424 }
425 }
426
427 public boolean isTagLibraryRegistered(String namespaceURI) {
428 boolean answer = taglibs.containsKey( namespaceURI );
429 if (answer) {
430 return true;
431 }
432 else if ( parent != null ) {
433 return parent.isTagLibraryRegistered(namespaceURI);
434 }
435 else {
436 return false;
437 }
438 }
439
440 /***
441 * @return the TagLibrary for the given namespace URI or null if one could not be found
442 */
443 public TagLibrary getTagLibrary(String namespaceURI) {
444
445
446
447
448 Object answer = taglibs.get(namespaceURI);
449
450 if ( answer == null && parent != null ) {
451 answer = parent.getTagLibrary( namespaceURI );
452 }
453
454 if ( answer instanceof TagLibrary ) {
455 return (TagLibrary) answer;
456 }
457 else if ( answer instanceof String ) {
458 String className = (String) answer;
459 Class theClass = null;
460 try {
461 theClass = getClassLoader().loadClass(className);
462 }
463 catch (ClassNotFoundException e) {
464 log.error("Could not find the class: " + className, e);
465 }
466 if ( theClass != null ) {
467 try {
468 Object object = theClass.newInstance();
469 if (object instanceof TagLibrary) {
470 taglibs.put(namespaceURI, object);
471 return (TagLibrary) object;
472 }
473 else {
474 log.error(
475 "The tag library object mapped to: "
476 + namespaceURI
477 + " is not a TagLibrary. Object = "
478 + object);
479 }
480 }
481 catch (Exception e) {
482 log.error(
483 "Could not instantiate instance of class: " + className + ". Reason: " + e,
484 e);
485 }
486 }
487 }
488
489 return null;
490 }
491
492 /***
493 * Attempts to parse the script from the given uri using the
494 * {@link #getResource} method then returns the compiled script.
495 */
496 public Script compileScript(String uri) throws JellyException {
497 XMLParser parser = getXMLParser();
498 parser.setContext(this);
499 InputStream in = getResourceAsStream(uri);
500 if (in == null) {
501 throw new JellyException("Could not find Jelly script: " + uri);
502 }
503 Script script = null;
504 try {
505 script = parser.parse(in);
506 } catch (IOException e) {
507 throw new JellyException(JellyContext.BAD_PARSE, e);
508 } catch (SAXException e) {
509 throw new JellyException(JellyContext.BAD_PARSE, e);
510 }
511
512 return script.compile();
513 }
514
515 /***
516 * Attempts to parse the script from the given URL using the
517 * {@link #getResource} method then returns the compiled script.
518 */
519 public Script compileScript(URL url) throws JellyException {
520 XMLParser parser = getXMLParser();
521 parser.setContext(this);
522
523 Script script = null;
524 try {
525 script = parser.parse(url.toString());
526 } catch (IOException e) {
527 throw new JellyException(JellyContext.BAD_PARSE, e);
528 } catch (SAXException e) {
529 throw new JellyException(JellyContext.BAD_PARSE, e);
530 }
531
532 return script.compile();
533 }
534
535 /***
536 * Attempts to parse the script from the given InputSource using the
537 * {@link #getResource} method then returns the compiled script.
538 */
539 public Script compileScript(InputSource source) throws JellyException {
540 XMLParser parser = getXMLParser();
541 parser.setContext(this);
542
543 Script script = null;
544 try {
545 script = parser.parse(source);
546 } catch (IOException e) {
547 throw new JellyException(JellyContext.BAD_PARSE, e);
548 } catch (SAXException e) {
549 throw new JellyException(JellyContext.BAD_PARSE, e);
550 }
551
552 return script.compile();
553 }
554
555 /***
556 * @return a thread pooled XMLParser to avoid the startup overhead
557 * of the XMLParser
558 */
559 protected XMLParser getXMLParser() {
560 XMLParser parser = createXMLParser();
561 return parser;
562 }
563
564 /***
565 * Factory method to allow JellyContext implementations to overload how an XMLParser
566 * is created - such as to overload what the default ExpressionFactory should be.
567 */
568 protected XMLParser createXMLParser() {
569 return new XMLParser();
570 }
571
572 /***
573 * Parses the script from the given File 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(File file, XMLOutput output) throws JellyException {
578 try {
579 return runScript(file.toURL(), output, JellyContext.DEFAULT_EXPORT,
580 JellyContext.DEFAULT_INHERIT);
581 } catch (MalformedURLException e) {
582 throw new JellyException(e.toString());
583 }
584 }
585
586 /***
587 * Parses the script from the given URL then compiles it and runs it.
588 *
589 * @return the new child context that was used to run the script
590 */
591 public JellyContext runScript(URL url, XMLOutput output) throws JellyException {
592 return runScript(url, output, JellyContext.DEFAULT_EXPORT,
593 JellyContext.DEFAULT_INHERIT);
594 }
595
596 /***
597 * Parses the script from the given InputSource then compiles it and runs it.
598 *
599 * @return the new child context that was used to run the script
600 */
601 public JellyContext runScript(InputSource source, XMLOutput output) throws JellyException {
602 return runScript(source, output, JellyContext.DEFAULT_EXPORT,
603 JellyContext.DEFAULT_INHERIT);
604 }
605
606 /***
607 * Parses the script from the given uri using the
608 * JellyContext.getResource() API then compiles it and runs it.
609 *
610 * @return the new child context that was used to run the script
611 */
612 public JellyContext runScript(String uri, XMLOutput output) throws JellyException {
613 URL url = null;
614 try {
615 url = getResource(uri);
616 } catch (MalformedURLException e) {
617 throw new JellyException(e.toString());
618 }
619
620 if (url == null) {
621 throw new JellyException("Could not find Jelly script: " + url);
622 }
623 return runScript(url, output, JellyContext.DEFAULT_EXPORT,
624 JellyContext.DEFAULT_INHERIT);
625 }
626
627 /***
628 * Parses the script from the given uri using the
629 * JellyContext.getResource() API then compiles it and runs it.
630 *
631 * @return the new child context that was used to run the script
632 */
633 public JellyContext runScript(String uri, XMLOutput output,
634 boolean export, boolean inherit) throws JellyException {
635 URL url = null;
636 try {
637 url = getResource(uri);
638 } catch (MalformedURLException e) {
639 throw new JellyException(e.toString());
640 }
641
642 if (url == null) {
643 throw new JellyException("Could not find Jelly script: " + url);
644 }
645
646 return runScript(url, output, export, inherit);
647 }
648
649 /***
650 * Parses the script from the given file 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(File file, XMLOutput output,
655 boolean export, boolean inherit) throws JellyException {
656 try {
657 return runScript(file.toURL(), output, export, inherit);
658 } catch (MalformedURLException e) {
659 throw new JellyException(e.toString());
660 }
661 }
662
663 /***
664 * Parses the script from the given URL then compiles it and runs it.
665 *
666 * @return the new child context that was used to run the script
667 */
668 public JellyContext runScript(URL url, XMLOutput output,
669 boolean export, boolean inherit) throws JellyException {
670 return runScript(new InputSource(url.toString()), output, export, inherit);
671 }
672
673 /***
674 * Parses the script from the given InputSource then compiles it and runs it.
675 *
676 * @return the new child context that was used to run the script
677 */
678 public JellyContext runScript(InputSource source, XMLOutput output,
679 boolean export, boolean inherit) throws JellyException {
680 Script script = compileScript(source);
681
682 URL newJellyContextURL = null;
683 try {
684 newJellyContextURL = getJellyContextURL(source);
685 } catch (MalformedURLException e) {
686 throw new JellyException(e.toString());
687 }
688
689 JellyContext newJellyContext = newJellyContext();
690 newJellyContext.setRootURL( newJellyContextURL );
691 newJellyContext.setCurrentURL( newJellyContextURL );
692 newJellyContext.setExport( export );
693 newJellyContext.setInherit( inherit );
694
695 if ( inherit ) {
696
697 newJellyContext.variables = this.variables;
698 }
699
700 if (log.isDebugEnabled() ) {
701 log.debug( "About to run script: " + source.getSystemId() );
702 log.debug( "root context URL: " + newJellyContext.rootURL );
703 log.debug( "current context URL: " + newJellyContext.currentURL );
704 }
705
706 script.run(newJellyContext, output);
707
708 return newJellyContext;
709 }
710
711 /***
712 * Returns a URL for the given resource from the specified path.
713 * If the uri starts with "/" then the path is taken as relative to
714 * the current context root.
715 * If the uri is a well formed URL then it is used.
716 * If the uri is a file that exists and can be read then it is used.
717 * Otherwise the uri is interpreted as relative to the current context (the
718 * location of the current script).
719 */
720 public URL getResource(String uri) throws MalformedURLException {
721 if (uri.startsWith("/")) {
722
723 return createRelativeURL(rootURL, uri.substring(1));
724 }
725 else {
726 try {
727 return new URL(uri);
728 }
729 catch (MalformedURLException e) {
730
731 try {
732 return createRelativeURL(currentURL, uri);
733 } catch (MalformedURLException e2) {
734 throw e;
735 }
736 }
737 }
738 }
739
740 /***
741 * Attempts to open an InputStream to the given resource at the specified path.
742 * If the uri starts with "/" then the path is taken as relative to
743 * the current context root. If the uri is a well formed URL then it
744 * is used. Otherwise the uri is interpreted as relative to the current
745 * context (the location of the current script).
746 *
747 * @return null if this resource could not be loaded, otherwise the resources
748 * input stream is returned.
749 */
750 public InputStream getResourceAsStream(String uri) {
751 try {
752 URL url = getResource(uri);
753 return url.openStream();
754 }
755 catch (Exception e) {
756 if (log.isTraceEnabled()) {
757 log.trace(
758 "Caught exception attempting to open: " + uri + ". Exception: " + e,
759 e);
760 }
761 return null;
762 }
763 }
764
765
766
767
768
769 /***
770 * @return the current root context URL from which all absolute resource URIs
771 * will be relative to. For example in a web application the root URL will
772 * map to the web directory which contains the WEB-INF directory.
773 */
774 public URL getRootURL() {
775 return rootURL;
776 }
777
778 /***
779 * Sets the current root context URL from which all absolute resource URIs
780 * will be relative to. For example in a web application the root URL will
781 * map to the web directory which contains the WEB-INF directory.
782 */
783 public void setRootURL(URL rootURL) {
784 this.rootURL = rootURL;
785 }
786
787
788 /***
789 * @return the current URL context of the current script that is executing.
790 * This URL context is used to deduce relative scripts when relative URIs are
791 * used in calls to {@link #getResource} to process relative scripts.
792 */
793 public URL getCurrentURL() {
794 return currentURL;
795 }
796
797 /***
798 * Sets the current URL context of the current script that is executing.
799 * This URL context is used to deduce relative scripts when relative URIs are
800 * used in calls to {@link #getResource} to process relative scripts.
801 */
802 public void setCurrentURL(URL currentURL) {
803 this.currentURL = currentURL;
804 }
805
806 /***
807 * Returns whether caching of Tag instances, per thread, is enabled.
808 * Caching Tags can boost performance, on some JVMs, by reducing the cost of
809 * object construction when running Jelly inside a multi-threaded application server
810 * such as a Servlet engine.
811 *
812 * @return whether caching of Tag instances is enabled.
813 */
814 public boolean isCacheTags() {
815 return cacheTags;
816 }
817
818 /***
819 * Sets whether caching of Tag instances, per thread, is enabled.
820 * Caching Tags can boost performance, on some JVMs, by reducing the cost of
821 * object construction when running Jelly inside a multi-threaded application server
822 * such as a Servlet engine.
823 *
824 * @param cacheTags Whether caching should be enabled or disabled.
825 */
826 public void setCacheTags(boolean cacheTags) {
827 this.cacheTags = cacheTags;
828 }
829
830 /***
831 * Returns whether we export tag libraries to our parents context
832 * @return boolean
833 */
834 public boolean isExportLibraries() {
835 return exportLibraries;
836 }
837
838 /***
839 * Sets whether we export tag libraries to our parents context
840 * @param exportLibraries The exportLibraries to set
841 */
842 public void setExportLibraries(boolean exportLibraries) {
843 this.exportLibraries = exportLibraries;
844 }
845
846
847 /***
848 * Sets whether we should export variable definitions to our parent context
849 */
850 public void setExport(boolean export) {
851 this.export = export;
852 }
853
854 /***
855 * @return whether we should export variable definitions to our parent context
856 */
857 public boolean isExport() {
858 return this.export;
859 }
860
861 /***
862 * Sets whether we should inherit variables from our parent context
863 */
864 public void setInherit(boolean inherit) {
865 this.inherit = inherit;
866 }
867
868 /***
869 * @return whether we should inherit variables from our parent context
870 */
871 public boolean isInherit() {
872 return this.inherit;
873 }
874
875
876 /***
877 * Return the class loader to be used for instantiating application objects
878 * when required. This is determined based upon the following rules:
879 * <ul>
880 * <li>The class loader set by <code>setClassLoader()</code>, if any</li>
881 * <li>The thread context class loader, if it exists and the
882 * <code>useContextClassLoader</code> property is set to true</li>
883 * <li>The class loader used to load the XMLParser class itself.
884 * </ul>
885 */
886 public ClassLoader getClassLoader() {
887 return ClassLoaderUtils.getClassLoader(classLoader, useContextClassLoader, getClass());
888 }
889
890 /***
891 * Set the class loader to be used for instantiating application objects
892 * when required.
893 *
894 * @param classLoader The new class loader to use, or <code>null</code>
895 * to revert to the standard rules
896 */
897 public void setClassLoader(ClassLoader classLoader) {
898 this.classLoader = classLoader;
899 }
900
901 /***
902 * Return the boolean as to whether the context classloader should be used.
903 */
904 public boolean getUseContextClassLoader() {
905 return useContextClassLoader;
906 }
907
908 /***
909 * Determine whether to use the Context ClassLoader (the one found by
910 * calling <code>Thread.currentThread().getContextClassLoader()</code>)
911 * to resolve/load classes. If not
912 * using Context ClassLoader, then the class-loading defaults to
913 * using the calling-class' ClassLoader.
914 *
915 * @param use determines whether to use JellyContext ClassLoader.
916 */
917 public void setUseContextClassLoader(boolean use) {
918 useContextClassLoader = use;
919 }
920
921
922
923
924 /***
925 * @return a new relative URL from the given root and with the addition of the
926 * extra relative URI
927 *
928 * @param rootURL is the root context from which the relative URI will be applied
929 * @param relativeURI is the relative URI (without a leading "/")
930 * @throws MalformedURLException if the URL is invalid.
931 */
932 protected URL createRelativeURL(URL rootURL, String relativeURI)
933 throws MalformedURLException {
934 URL url = rootURL;
935 if (url == null) {
936 File file = new File(System.getProperty("user.dir"));
937 url = file.toURL();
938 }
939 String urlText = url.toString() + relativeURI;
940 if ( log.isDebugEnabled() ) {
941 log.debug("Attempting to open url: " + urlText);
942 }
943 return new URL(urlText);
944 }
945
946 /***
947 * Strips off the name of a script to create a new context URL
948 */
949 protected URL getJellyContextURL(URL url) throws MalformedURLException {
950 String text = url.toString();
951 int idx = text.lastIndexOf('/');
952 text = text.substring(0, idx + 1);
953 return new URL(text);
954 }
955
956 /***
957 * Strips off the name of a script to create a new context URL
958 */
959 protected URL getJellyContextURL(InputSource source) throws MalformedURLException {
960 String text = source.getSystemId();
961 if (text != null) {
962 int idx = text.lastIndexOf('/');
963 text = text.substring(0, idx + 1);
964 return new URL(text);
965 } else {
966 return null;
967 }
968
969 }
970
971 /***
972 * Factory method to create a new child of this context
973 */
974 protected JellyContext createChildContext() {
975 return new JellyContext(this);
976 }
977
978 /***
979 * Change the parent context to the one provided
980 * @param context the new parent context
981 */
982 protected void setParent(JellyContext context)
983 {
984 parent = context;
985 this.variables.put("parentScope", parent.variables);
986
987 if (isExportLibraries() && parent != null) {
988 for (Iterator keys = taglibs.keySet().iterator(); keys.hasNext();)
989 {
990 String namespaceURI = (String) keys.next();
991 Object tagLibOrClassName = taglibs.get(namespaceURI);
992 if (tagLibOrClassName instanceof TagLibrary)
993 {
994 parent.registerTagLibrary( namespaceURI, (TagLibrary) tagLibOrClassName );
995 }
996 else
997 {
998 parent.registerTagLibrary( namespaceURI, (String) tagLibOrClassName );
999 }
1000 }
1001 }
1002
1003 }
1004
1005 }