001: /*
002: * Copyright 2002,2004 The Apache Software Foundation.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package org.apache.commons.jelly;
017:
018: import java.io.File;
019: import java.io.IOException;
020: import java.io.InputStream;
021: import java.net.MalformedURLException;
022: import java.net.URL;
023: import java.util.Collections;
024: import java.util.HashMap;
025: import java.util.Hashtable;
026: import java.util.Iterator;
027: import java.util.Map;
028: import java.util.WeakHashMap;
029:
030: import org.apache.commons.jelly.parser.XMLParser;
031: import org.apache.commons.jelly.util.ClassLoaderUtils;
032: import org.apache.commons.logging.Log;
033: import org.apache.commons.logging.LogFactory;
034: import org.xml.sax.InputSource;
035: import org.xml.sax.SAXException;
036:
037: /**
038: * <p><code>JellyContext</code> represents the Jelly context.</p>
039: *
040: * @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
041: * @version $Revision: 165507 $
042: */
043: public class JellyContext {
044:
045: /** The Log to which logging calls will be made. */
046: private static final Log log = LogFactory
047: .getLog(JellyContext.class);
048:
049: /** Default for inheritance of variables **/
050: private static final boolean DEFAULT_INHERIT = true;
051:
052: /** Default for export of variables **/
053: private static final boolean DEFAULT_EXPORT = false;
054:
055: /** String used to denote a script can't be parsed */
056: private static final String BAD_PARSE = "Could not parse Jelly script";
057:
058: /**
059: * The class loader to use for instantiating application objects.
060: * If not specified, the context class loader, or the class loader
061: * used to load this class itself, is used, based on the value of the
062: * <code>useContextClassLoader</code> variable.
063: */
064: protected ClassLoader classLoader;
065:
066: /**
067: * Do we want to use the Context ClassLoader when loading classes
068: * for instantiating new objects? Default is <code>false</code>.
069: */
070: protected boolean useContextClassLoader = false;
071:
072: /** The root URL context (where scripts are located from) */
073: private URL rootURL;
074:
075: /** The current URL context (where relative scripts are located from) */
076: private URL currentURL;
077:
078: /** Tag libraries found so far */
079: private Map taglibs = new Hashtable();
080:
081: /** synchronized access to the variables in scope */
082: private Map variables = new Hashtable();
083:
084: /** The parent context */
085: private JellyContext parent;
086:
087: /** Do we inherit variables from parent context? */
088: private boolean inherit = JellyContext.DEFAULT_INHERIT;
089:
090: /** Do we export our variables to parent context? */
091: private boolean export = JellyContext.DEFAULT_EXPORT;
092:
093: /** Should we export tag libraries to our parents context */
094: private boolean exportLibraries = true;
095:
096: /**
097: * Create a new context with the currentURL set to the rootURL
098: */
099: 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: * Create a new context with the given parent context.
125: * The parent's rootURL and currentURL are set on the child, and the parent's variables are
126: * available in the child context under the name <code>parentScope</code>.
127: *
128: * @param parent the parent context for the newly created context.
129: */
130: public JellyContext(JellyContext parent) {
131: this .parent = parent;
132: this .rootURL = parent.rootURL;
133: this .currentURL = parent.currentURL;
134: this .variables.put("parentScope", parent.variables);
135: init();
136: }
137:
138: /**
139: * Create a new context with the given parent context.
140: * The parent's rootURL are set on the child, and the parent's variables are
141: * available in the child context under the name <code>parentScope</code>.
142: *
143: * @param parentJellyContext the parent context for the newly created context.
144: * @param currentURL the root URL used in resolving relative resources
145: */
146: public JellyContext(JellyContext parentJellyContext, URL currentURL) {
147: this (parentJellyContext);
148: this .currentURL = currentURL;
149: }
150:
151: /**
152: * Create a new context with the given parent context.
153: * The parent's variables are available in the child context under the name <code>parentScope</code>.
154: *
155: * @param parentJellyContext the parent context for the newly created context.
156: * @param rootURL the root URL used in resolving absolute resources i.e. those starting with '/'
157: * @param currentURL the root URL used in resolving relative resources
158: */
159: public JellyContext(JellyContext parentJellyContext, URL rootURL,
160: URL currentURL) {
161: this (parentJellyContext, currentURL);
162: this .rootURL = rootURL;
163: }
164:
165: /**
166: * Initialize the context.
167: * This includes adding the context to itself under the name <code>context</code> and
168: * making the System Properties available as <code>systemScope</code>
169: */
170: private void init() {
171: variables.put("context", this );
172: try {
173: variables.put("systemScope", System.getProperties());
174: } catch (SecurityException e) {
175: log.debug("security exception accessing system properties",
176: 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
210: || variables.containsKey(name);
211:
212: if (definedHere)
213: return answer;
214:
215: if (answer == null && parent != null) {
216: answer = parent.findVariable(name);
217: }
218: // ### this is a hack - remove this when we have support for pluggable Scopes
219: if (answer == null) {
220: answer = getSystemProperty(name);
221: }
222:
223: if (log.isDebugEnabled()) {
224: log.debug("findVariable: " + name + " value: " + answer);
225: }
226: return answer;
227: }
228:
229: /** @return the value of the given variable name */
230: public Object getVariable(String name) {
231: Object value = variables.get(name);
232: boolean definedHere = value != null
233: || variables.containsKey(name);
234:
235: if (definedHere)
236: return value;
237:
238: if (value == null && isInherit()) {
239: JellyContext parentContext = getParent();
240: if (parentContext != null) {
241: value = parentContext.getVariable(name);
242: }
243: }
244:
245: // ### this is a hack - remove this when we have support for pluggable Scopes
246: if (value == null) {
247: value = getSystemProperty(name);
248: }
249:
250: return value;
251: }
252:
253: /**
254: * Get a system property and handle security exceptions
255: * @param name the name of the property to retrieve
256: * @return the value of the property, or null if a SecurityException occurs
257: */
258: private Object getSystemProperty(String name) {
259: try {
260: return System.getProperty(name);
261: } catch (SecurityException e) {
262: log.debug("security exception accessing system properties",
263: e);
264: }
265: return null;
266: }
267:
268: /**
269: * @return the value of the given variable name in the given variable scope
270: * @param name is the name of the variable
271: * @param scopeName is the optional scope name such as 'parent'. For servlet environments
272: * this could be 'application', 'session' or 'request'.
273: */
274: public Object getVariable(String name, String scopeName) {
275: JellyContext scope = getScope(scopeName);
276: if (scope != null) {
277: return scope.getVariable(name);
278: }
279: return null;
280: }
281:
282: /** Sets the value of the named variable */
283: public void setVariable(String name, Object value) {
284: if (isExport()) {
285: getParent().setVariable(name, value);
286: return;
287: }
288: if (value == null) {
289: variables.remove(name);
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: // I have seen this fail when the passed Map contains a key, value
348: // pair where the value is null
349: for (Iterator iter = variables.entrySet().iterator(); iter
350: .hasNext();) {
351: Map.Entry element = (Map.Entry) iter.next();
352: if (element.getValue() != null) {
353: this .variables
354: .put(element.getKey(), element.getValue());
355: }
356: }
357: //this.variables.putAll( variables );
358: }
359:
360: /**
361: * A factory method to create a new child context of the
362: * current context.
363: */
364: public JellyContext newJellyContext(Map newVariables) {
365: // XXXX: should allow this new context to
366: // XXXX: inherit parent contexts?
367: // XXXX: Or at least publish the parent scope
368: // XXXX: as a Map in this new variable scope?
369: newVariables.put("parentScope", variables);
370: JellyContext answer = createChildContext();
371: answer.setVariables(newVariables);
372: return answer;
373: }
374:
375: /**
376: * A factory method to create a new child context of the
377: * current context.
378: */
379: public JellyContext newJellyContext() {
380: return createChildContext();
381: }
382:
383: /** Clears variables set by Tags.
384: * @see #clearVariables()
385: */
386: public void clear() {
387: clearVariables();
388: }
389:
390: /** Clears variables set by Tags (variables set while running a Jelly script)
391: * @see #clear()
392: */
393: protected void clearVariables() {
394: variables.clear();
395: }
396:
397: /** Registers the given tag library against the given namespace URI.
398: * This should be called before the parser is used.
399: */
400: public void registerTagLibrary(String namespaceURI,
401: TagLibrary taglib) {
402: if (log.isDebugEnabled()) {
403: log.debug("Registering tag library to: " + namespaceURI
404: + " taglib: " + taglib);
405: }
406: taglibs.put(namespaceURI, taglib);
407:
408: if (isExportLibraries() && parent != null) {
409: parent.registerTagLibrary(namespaceURI, taglib);
410: }
411: }
412:
413: /** Registers the given tag library class name against the given namespace URI.
414: * The class will be loaded via the given ClassLoader
415: * This should be called before the parser is used.
416: */
417: public void registerTagLibrary(String namespaceURI, String className) {
418:
419: if (log.isDebugEnabled()) {
420: log.debug("Registering tag library to: " + namespaceURI
421: + " taglib: " + className);
422: }
423: taglibs.put(namespaceURI, className);
424:
425: if (isExportLibraries() && parent != null) {
426: parent.registerTagLibrary(namespaceURI, className);
427: }
428: }
429:
430: public boolean isTagLibraryRegistered(String namespaceURI) {
431: boolean answer = taglibs.containsKey(namespaceURI);
432: if (answer) {
433: return true;
434: } else if (parent != null) {
435: return parent.isTagLibraryRegistered(namespaceURI);
436: } else {
437: return false;
438: }
439: }
440:
441: /**
442: * @return the TagLibrary for the given namespace URI or null if one could not be found
443: */
444: public TagLibrary getTagLibrary(String namespaceURI) {
445:
446: // use my own mapping first, so that namespaceURIs can
447: // be redefined inside child contexts...
448:
449: Object answer = taglibs.get(namespaceURI);
450:
451: if (answer == null && parent != null) {
452: answer = parent.getTagLibrary(namespaceURI);
453: }
454:
455: if (answer instanceof TagLibrary) {
456: return (TagLibrary) answer;
457: } else if (answer instanceof String) {
458: String className = (String) answer;
459: Class theClass = null;
460: try {
461: theClass = getClassLoader().loadClass(className);
462: } catch (ClassNotFoundException e) {
463: log.error("Could not find the class: " + className, e);
464: }
465: if (theClass != null) {
466: try {
467: Object object = theClass.newInstance();
468: if (object instanceof TagLibrary) {
469: taglibs.put(namespaceURI, object);
470: return (TagLibrary) object;
471: } else {
472: log.error("The tag library object mapped to: "
473: + namespaceURI
474: + " is not a TagLibrary. Object = "
475: + object);
476: }
477: } catch (Exception e) {
478: log.error(
479: "Could not instantiate instance of class: "
480: + className + ". Reason: " + e, e);
481: }
482: }
483: }
484:
485: return null;
486: }
487:
488: /**
489: * Attempts to parse the script from the given uri using the
490: * {@link #getResource} method then returns the compiled script.
491: */
492: public Script compileScript(String uri) throws JellyException {
493: XMLParser parser = getXMLParser();
494: parser.setContext(this );
495: InputStream in = getResourceAsStream(uri);
496: if (in == null) {
497: throw new JellyException("Could not find Jelly script: "
498: + uri);
499: }
500: Script script = null;
501: try {
502: script = parser.parse(in);
503: } catch (IOException e) {
504: throw new JellyException(JellyContext.BAD_PARSE, e);
505: } catch (SAXException e) {
506: throw new JellyException(JellyContext.BAD_PARSE, e);
507: }
508:
509: return script.compile();
510: }
511:
512: /**
513: * Attempts to parse the script from the given URL using the
514: * {@link #getResource} method then returns the compiled script.
515: */
516: public Script compileScript(URL url) throws JellyException {
517: XMLParser parser = getXMLParser();
518: parser.setContext(this );
519:
520: Script script = null;
521: try {
522: script = parser.parse(url.toString());
523: } catch (IOException e) {
524: throw new JellyException(JellyContext.BAD_PARSE, e);
525: } catch (SAXException e) {
526: throw new JellyException(JellyContext.BAD_PARSE, e);
527: }
528:
529: return script.compile();
530: }
531:
532: /**
533: * Attempts to parse the script from the given InputSource using the
534: * {@link #getResource} method then returns the compiled script.
535: */
536: public Script compileScript(InputSource source)
537: throws JellyException {
538: XMLParser parser = getXMLParser();
539: parser.setContext(this );
540:
541: Script script = null;
542: try {
543: script = parser.parse(source);
544: } catch (IOException e) {
545: throw new JellyException(JellyContext.BAD_PARSE, e);
546: } catch (SAXException e) {
547: throw new JellyException(JellyContext.BAD_PARSE, e);
548: }
549:
550: return script.compile();
551: }
552:
553: /**
554: * @return a thread pooled XMLParser to avoid the startup overhead
555: * of the XMLParser
556: */
557: protected XMLParser getXMLParser() {
558: XMLParser parser = createXMLParser();
559: return parser;
560: }
561:
562: /**
563: * Factory method to allow JellyContext implementations to overload how an XMLParser
564: * is created - such as to overload what the default ExpressionFactory should be.
565: */
566: protected XMLParser createXMLParser() {
567: return new XMLParser();
568: }
569:
570: /**
571: * Parses the script from the given File then compiles it and runs it.
572: *
573: * @return the new child context that was used to run the script
574: */
575: public JellyContext runScript(File file, XMLOutput output)
576: throws JellyException {
577: try {
578: return runScript(file.toURL(), output,
579: 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)
592: throws JellyException {
593: return runScript(url, output, JellyContext.DEFAULT_EXPORT,
594: JellyContext.DEFAULT_INHERIT);
595: }
596:
597: /**
598: * Parses the script from the given InputSource then compiles it and runs it.
599: *
600: * @return the new child context that was used to run the script
601: */
602: public JellyContext runScript(InputSource source, XMLOutput output)
603: throws JellyException {
604: return runScript(source, output, JellyContext.DEFAULT_EXPORT,
605: JellyContext.DEFAULT_INHERIT);
606: }
607:
608: /**
609: * Parses the script from the given uri using the
610: * JellyContext.getResource() API then compiles it and runs it.
611: *
612: * @return the new child context that was used to run the script
613: */
614: public JellyContext runScript(String uri, XMLOutput output)
615: throws JellyException {
616: URL url = null;
617: try {
618: url = getResource(uri);
619: } catch (MalformedURLException e) {
620: throw new JellyException(e.toString());
621: }
622:
623: if (url == null) {
624: throw new JellyException("Could not find Jelly script: "
625: + url);
626: }
627: return runScript(url, output, JellyContext.DEFAULT_EXPORT,
628: JellyContext.DEFAULT_INHERIT);
629: }
630:
631: /**
632: * Parses the script from the given uri using the
633: * JellyContext.getResource() API then compiles it and runs it.
634: *
635: * @return the new child context that was used to run the script
636: */
637: public JellyContext runScript(String uri, XMLOutput output,
638: boolean export, boolean inherit) throws JellyException {
639: URL url = null;
640: try {
641: url = getResource(uri);
642: } catch (MalformedURLException e) {
643: throw new JellyException(e.toString());
644: }
645:
646: if (url == null) {
647: throw new JellyException("Could not find Jelly script: "
648: + url);
649: }
650:
651: return runScript(url, output, export, inherit);
652: }
653:
654: /**
655: * Parses the script from the given file then compiles it and runs it.
656: *
657: * @return the new child context that was used to run the script
658: */
659: public JellyContext runScript(File file, XMLOutput output,
660: boolean export, boolean inherit) throws JellyException {
661: try {
662: return runScript(file.toURL(), output, export, inherit);
663: } catch (MalformedURLException e) {
664: throw new JellyException(e.toString());
665: }
666: }
667:
668: /**
669: * Parses the script from the given URL then compiles it and runs it.
670: *
671: * @return the new child context that was used to run the script
672: */
673: public JellyContext runScript(URL url, XMLOutput output,
674: boolean export, boolean inherit) throws JellyException {
675: return runScript(new InputSource(url.toString()), output,
676: export, inherit);
677: }
678:
679: /**
680: * Parses the script from the given InputSource then compiles it and runs it.
681: *
682: * @return the new child context that was used to run the script
683: */
684: public JellyContext runScript(InputSource source, XMLOutput output,
685: boolean export, boolean inherit) throws JellyException {
686: Script script = compileScript(source);
687:
688: URL newJellyContextURL = null;
689: try {
690: newJellyContextURL = getJellyContextURL(source);
691: } catch (MalformedURLException e) {
692: throw new JellyException(e.toString());
693: }
694:
695: JellyContext newJellyContext = newJellyContext();
696: newJellyContext.setRootURL(newJellyContextURL);
697: newJellyContext.setCurrentURL(newJellyContextURL);
698: newJellyContext.setExport(export);
699: newJellyContext.setInherit(inherit);
700:
701: if (inherit) {
702: // use the same variable scopes
703: newJellyContext.variables = this .variables;
704: }
705:
706: if (log.isDebugEnabled()) {
707: log.debug("About to run script: " + source.getSystemId());
708: log.debug("root context URL: " + newJellyContext.rootURL);
709: log.debug("current context URL: "
710: + newJellyContext.currentURL);
711: }
712:
713: script.run(newJellyContext, output);
714:
715: return newJellyContext;
716: }
717:
718: /**
719: * Returns a URL for the given resource from the specified path.
720: * If the uri starts with "/" then the path is taken as relative to
721: * the current context root.
722: * If the uri is a well formed URL then it is used.
723: * If the uri is a file that exists and can be read then it is used.
724: * Otherwise the uri is interpreted as relative to the current context (the
725: * location of the current script).
726: */
727: public URL getResource(String uri) throws MalformedURLException {
728: if (uri.startsWith("/")) {
729: // append this uri to the context root
730: return createRelativeURL(rootURL, uri.substring(1));
731: } else {
732: try {
733: return new URL(uri);
734: } catch (MalformedURLException e) {
735: // lets try find a relative resource
736: try {
737: return createRelativeURL(currentURL, uri);
738: } catch (MalformedURLException e2) {
739: throw e;
740: }
741: }
742: }
743: }
744:
745: /**
746: * Attempts to open an InputStream to the given resource at the specified path.
747: * If the uri starts with "/" then the path is taken as relative to
748: * the current context root. If the uri is a well formed URL then it
749: * is used. Otherwise the uri is interpreted as relative to the current
750: * context (the location of the current script).
751: *
752: * @return null if this resource could not be loaded, otherwise the resources
753: * input stream is returned.
754: */
755: public InputStream getResourceAsStream(String uri) {
756: try {
757: URL url = getResource(uri);
758: return url.openStream();
759: } catch (Exception e) {
760: if (log.isTraceEnabled()) {
761: log.trace("Caught exception attempting to open: " + uri
762: + ". Exception: " + e, e);
763: }
764: return null;
765: }
766: }
767:
768: // Properties
769: //-------------------------------------------------------------------------
770:
771: /**
772: * @return the current root context URL from which all absolute resource URIs
773: * will be relative to. For example in a web application the root URL will
774: * map to the web directory which contains the WEB-INF directory.
775: */
776: public URL getRootURL() {
777: return rootURL;
778: }
779:
780: /**
781: * Sets the current root context URL from which all absolute resource URIs
782: * will be relative to. For example in a web application the root URL will
783: * map to the web directory which contains the WEB-INF directory.
784: */
785: public void setRootURL(URL rootURL) {
786: this .rootURL = rootURL;
787: }
788:
789: /**
790: * @return the current URL context of the current script that is executing.
791: * This URL context is used to deduce relative scripts when relative URIs are
792: * used in calls to {@link #getResource} to process relative scripts.
793: */
794: public URL getCurrentURL() {
795: return currentURL;
796: }
797:
798: /**
799: * Sets the current URL context of the current script that is executing.
800: * This URL context is used to deduce relative scripts when relative URIs are
801: * used in calls to {@link #getResource} to process relative scripts.
802: */
803: public void setCurrentURL(URL currentURL) {
804: this .currentURL = currentURL;
805: }
806:
807: /**
808: * Returns whether we export tag libraries to our parents context
809: * @return boolean
810: */
811: public boolean isExportLibraries() {
812: return exportLibraries;
813: }
814:
815: /**
816: * Sets whether we export tag libraries to our parents context
817: * @param exportLibraries The exportLibraries to set
818: */
819: public void setExportLibraries(boolean exportLibraries) {
820: this .exportLibraries = exportLibraries;
821: }
822:
823: /**
824: * Sets whether we should export variable definitions to our parent context
825: */
826: public void setExport(boolean export) {
827: this .export = export;
828: }
829:
830: /**
831: * @return whether we should export variable definitions to our parent context
832: */
833: public boolean isExport() {
834: return this .export;
835: }
836:
837: /**
838: * Sets whether we should inherit variables from our parent context
839: */
840: public void setInherit(boolean inherit) {
841: this .inherit = inherit;
842: }
843:
844: /**
845: * @return whether we should inherit variables from our parent context
846: */
847: public boolean isInherit() {
848: return this .inherit;
849: }
850:
851: /**
852: * Return the class loader to be used for instantiating application objects
853: * when required. This is determined based upon the following rules:
854: * <ul>
855: * <li>The class loader set by <code>setClassLoader()</code>, if any</li>
856: * <li>The thread context class loader, if it exists and the
857: * <code>useContextClassLoader</code> property is set to true</li>
858: * <li>The class loader used to load the XMLParser class itself.
859: * </ul>
860: */
861: public ClassLoader getClassLoader() {
862: return ClassLoaderUtils.getClassLoader(classLoader,
863: useContextClassLoader, getClass());
864: }
865:
866: /**
867: * Set the class loader to be used for instantiating application objects
868: * when required.
869: *
870: * @param classLoader The new class loader to use, or <code>null</code>
871: * to revert to the standard rules
872: */
873: public void setClassLoader(ClassLoader classLoader) {
874: this .classLoader = classLoader;
875: }
876:
877: /**
878: * Return the boolean as to whether the context classloader should be used.
879: */
880: public boolean getUseContextClassLoader() {
881: return useContextClassLoader;
882: }
883:
884: /**
885: * Determine whether to use the Context ClassLoader (the one found by
886: * calling <code>Thread.currentThread().getContextClassLoader()</code>)
887: * to resolve/load classes. If not
888: * using Context ClassLoader, then the class-loading defaults to
889: * using the calling-class' ClassLoader.
890: *
891: * @param use determines whether to use JellyContext ClassLoader.
892: */
893: public void setUseContextClassLoader(boolean use) {
894: useContextClassLoader = use;
895: }
896:
897: // Implementation methods
898: //-------------------------------------------------------------------------
899: /**
900: * @return a new relative URL from the given root and with the addition of the
901: * extra relative URI
902: *
903: * @param rootURL is the root context from which the relative URI will be applied
904: * @param relativeURI is the relative URI (without a leading "/")
905: * @throws MalformedURLException if the URL is invalid.
906: */
907: protected URL createRelativeURL(URL rootURL, String relativeURI)
908: throws MalformedURLException {
909: URL url = rootURL;
910: if (url == null) {
911: File file = new File(System.getProperty("user.dir"));
912: url = file.toURL();
913: }
914: String urlText = url.toString() + relativeURI;
915: if (log.isDebugEnabled()) {
916: log.debug("Attempting to open url: " + urlText);
917: }
918: return new URL(urlText);
919: }
920:
921: /**
922: * Strips off the name of a script to create a new context URL
923: */
924: protected URL getJellyContextURL(URL url)
925: throws MalformedURLException {
926: String text = url.toString();
927: int idx = text.lastIndexOf('/');
928: text = text.substring(0, idx + 1);
929: return new URL(text);
930: }
931:
932: /**
933: * Strips off the name of a script to create a new context URL
934: */
935: protected URL getJellyContextURL(InputSource source)
936: throws MalformedURLException {
937: String text = source.getSystemId();
938: if (text != null) {
939: int idx = text.lastIndexOf('/');
940: text = text.substring(0, idx + 1);
941: return new URL(text);
942: } else {
943: return null;
944: }
945:
946: }
947:
948: /**
949: * Factory method to create a new child of this context
950: */
951: protected JellyContext createChildContext() {
952: return new JellyContext(this );
953: }
954:
955: /**
956: * Change the parent context to the one provided
957: * @param context the new parent context
958: */
959: protected void setParent(JellyContext context) {
960: parent = context;
961: this .variables.put("parentScope", parent.variables);
962: // need to re-export tag libraries to the new parent
963: if (isExportLibraries() && parent != null) {
964: for (Iterator keys = taglibs.keySet().iterator(); keys
965: .hasNext();) {
966: String namespaceURI = (String) keys.next();
967: Object tagLibOrClassName = taglibs.get(namespaceURI);
968: if (tagLibOrClassName instanceof TagLibrary) {
969: parent.registerTagLibrary(namespaceURI,
970: (TagLibrary) tagLibOrClassName);
971: } else {
972: parent.registerTagLibrary(namespaceURI,
973: (String) tagLibOrClassName);
974: }
975: }
976: }
977:
978: }
979:
980: }
|