| /* |
| * Copyright 2002,2004 The Apache Software Foundation. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.commons.jelly; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.net.MalformedURLException; |
| import java.net.URL; |
| import java.util.HashMap; |
| import java.util.Hashtable; |
| import java.util.Iterator; |
| import java.util.Map; |
| |
| import org.apache.commons.jelly.parser.XMLParser; |
| import org.apache.commons.jelly.util.ClassLoaderUtils; |
| import org.apache.commons.logging.Log; |
| import org.apache.commons.logging.LogFactory; |
| import org.xml.sax.InputSource; |
| import org.xml.sax.SAXException; |
| |
| /** |
| * <p><code>JellyContext</code> represents the Jelly context.</p> |
| * |
| * @author <a href="mailto:jstrachan@apache.org">James Strachan</a> |
| * @version $Revision: 1.10 $ |
| */ |
| public class JellyContext { |
| |
| /** The Log to which logging calls will be made. */ |
| private static final Log log = LogFactory.getLog(JellyContext.class); |
| |
| /** Default for inheritance of variables **/ |
| private static final boolean DEFAULT_INHERIT = true; |
| |
| /** Default for export of variables **/ |
| private static final boolean DEFAULT_EXPORT = false; |
| |
| /** String used to denote a script can't be parsed */ |
| private static final String BAD_PARSE = "Could not parse Jelly script"; |
| |
| /** |
| * The class loader to use for instantiating application objects. |
| * If not specified, the context class loader, or the class loader |
| * used to load this class itself, is used, based on the value of the |
| * <code>useContextClassLoader</code> variable. |
| */ |
| protected ClassLoader classLoader; |
| |
| /** |
| * Do we want to use the Context ClassLoader when loading classes |
| * for instantiating new objects? Default is <code>false</code>. |
| */ |
| protected boolean useContextClassLoader = false; |
| |
| /** The root URL context (where scripts are located from) */ |
| private URL rootURL; |
| |
| /** The current URL context (where relative scripts are located from) */ |
| private URL currentURL; |
| |
| /** Tag libraries found so far */ |
| private Map taglibs = new Hashtable(); |
| |
| /** synchronized access to the variables in scope */ |
| private Map variables = new Hashtable(); |
| |
| /** The parent context */ |
| private JellyContext parent; |
| |
| /** Do we inherit variables from parent context? */ |
| private boolean inherit = JellyContext.DEFAULT_INHERIT; |
| |
| /** Do we export our variables to parent context? */ |
| private boolean export = JellyContext.DEFAULT_EXPORT; |
| |
| /** Should we export tag libraries to our parents context */ |
| private boolean exportLibraries = true; |
| |
| /** Holds a Map containing thread-specific data for Scripts. |
| * Scripts may hold a single object in this map using the |
| * {@link #setThreadLocalScriptData(Script,Object)} |
| */ |
| private ThreadLocal threadLocalScriptData = new ThreadLocal(); |
| // THINKME: Script objects are like Object (for equals and hashCode) I think. |
| // It should be asy to optimize hash-map distribution, e.g. by |
| // shifting the hashcode return value (presuming Object.hashcode() |
| // is something like an address) |
| |
| /** |
| * Create a new context with the currentURL set to the rootURL |
| */ |
| public JellyContext() { |
| this.currentURL = rootURL; |
| init(); |
| } |
| |
| /** |
| * Create a new context with the given rootURL |
| * @param rootURL the root URL used in resolving absolute resources i.e. those starting with '/' |
| */ |
| public JellyContext(URL rootURL) { |
| this( rootURL, rootURL ); |
| } |
| |
| /** |
| * Create a new context with the given rootURL and currentURL |
| * @param rootURL the root URL used in resolving absolute resources i.e. those starting with '/' |
| * @param currentURL the root URL used in resolving relative resources |
| */ |
| public JellyContext(URL rootURL, URL currentURL) { |
| this.rootURL = rootURL; |
| this.currentURL = currentURL; |
| init(); |
| } |
| |
| |
| /** |
| * Create a new context with the given parent context. |
| * The parent's rootURL and currentURL are set on the child, and the parent's variables are |
| * available in the child context under the name <code>parentScope</code>. |
| * |
| * @param parent the parent context for the newly created context. |
| */ |
| public JellyContext(JellyContext parent) { |
| this.parent = parent; |
| this.rootURL = parent.rootURL; |
| this.currentURL = parent.currentURL; |
| this.variables.put("parentScope", parent.variables); |
| init(); |
| } |
| |
| /** |
| * Create a new context with the given parent context. |
| * The parent's rootURL are set on the child, and the parent's variables are |
| * available in the child context under the name <code>parentScope</code>. |
| * |
| * @param parentJellyContext the parent context for the newly created context. |
| * @param currentURL the root URL used in resolving relative resources |
| */ |
| public JellyContext(JellyContext parentJellyContext, URL currentURL) { |
| this(parentJellyContext); |
| this.currentURL = currentURL; |
| } |
| |
| /** |
| * Create a new context with the given parent context. |
| * The parent's variables are available in the child context under the name <code>parentScope</code>. |
| * |
| * @param parentJellyContext the parent context for the newly created context. |
| * @param rootURL the root URL used in resolving absolute resources i.e. those starting with '/' |
| * @param currentURL the root URL used in resolving relative resources |
| */ |
| public JellyContext(JellyContext parentJellyContext, URL rootURL, URL currentURL) { |
| this(parentJellyContext, currentURL); |
| this.rootURL = rootURL; |
| } |
| |
| /** |
| * Initialize the context. |
| * This includes adding the context to itself under the name <code>context</code> and |
| * making the System Properties available as <code>systemScope</code> |
| */ |
| private void init() { |
| variables.put("context",this); |
| try { |
| variables.put("systemScope", System.getProperties() ); |
| } catch (SecurityException e) { |
| log.debug("security exception accessing system properties", e); |
| } |
| } |
| |
| /** |
| * @return the parent context for this context |
| */ |
| public JellyContext getParent() { |
| return parent; |
| } |
| |
| /** |
| * @return the scope of the given name, such as the 'parent' scope. |
| * If Jelly is used in a Servlet situation then 'request', 'session' and 'application' are other names |
| * for scopes |
| */ |
| public JellyContext getScope(String name) { |
| if ( "parent".equals( name ) ) { |
| return getParent(); |
| } |
| return null; |
| } |
| |
| /** |
| * Finds the variable value of the given name in this context or in any other parent context. |
| * If this context does not contain the variable, then its parent is used and then its parent |
| * and so forth until the context with no parent is found. |
| * |
| * @return the value of the variable in this or one of its descendant contexts or null |
| * if the variable could not be found. |
| */ |
| public Object findVariable(String name) { |
| Object answer = variables.get(name); |
| boolean definedHere = answer != null || variables.containsKey(name); |
| |
| if (definedHere) return answer; |
| |
| if ( answer == null && parent != null ) { |
| answer = parent.findVariable(name); |
| } |
| // ### this is a hack - remove this when we have support for pluggable Scopes |
| if ( answer == null ) { |
| answer = getSystemProperty(name); |
| } |
| |
| if (log.isDebugEnabled()) { |
| log.debug("findVariable: " + name + " value: " + answer ); |
| } |
| return answer; |
| } |
| |
| |
| /** @return the value of the given variable name */ |
| public Object getVariable(String name) { |
| Object value = variables.get(name); |
| boolean definedHere = value != null || variables.containsKey(name); |
| |
| if (definedHere) return value; |
| |
| if ( value == null && isInherit() ) { |
| JellyContext parentContext = getParent(); |
| if (parentContext != null) { |
| value = parentContext.getVariable( name ); |
| } |
| } |
| |
| // ### this is a hack - remove this when we have support for pluggable Scopes |
| if ( value == null ) { |
| value = getSystemProperty(name); |
| } |
| |
| return value; |
| } |
| |
| /** |
| * Get a system property and handle security exceptions |
| * @param name the name of the property to retrieve |
| * @return the value of the property, or null if a SecurityException occurs |
| */ |
| private Object getSystemProperty(String name) { |
| try { |
| return System.getProperty(name); |
| } |
| catch (SecurityException e) { |
| log.debug("security exception accessing system properties", e); |
| } |
| return null; |
| } |
| |
| /** |
| * @return the value of the given variable name in the given variable scope |
| * @param name is the name of the variable |
| * @param scopeName is the optional scope name such as 'parent'. For servlet environments |
| * this could be 'application', 'session' or 'request'. |
| */ |
| public Object getVariable(String name, String scopeName) { |
| JellyContext scope = getScope(scopeName); |
| if ( scope != null ) { |
| return scope.getVariable(name); |
| } |
| return null; |
| } |
| |
| |
| |
| /** Sets the value of the named variable */ |
| public void setVariable(String name, Object value) { |
| if ( isExport() ) { |
| getParent().setVariable( name, value ); |
| return; |
| } |
| if (value == null) { |
| variables.remove(name); |
| } |
| else { |
| variables.put(name, value); |
| } |
| } |
| |
| /** |
| * Sets the value of the given variable name in the given variable scope |
| * @param name is the name of the variable |
| * @param scopeName is the optional scope name such as 'parent'. For servlet environments |
| * this could be 'application', 'session' or 'request'. |
| * @param value is the value of the attribute |
| */ |
| public void setVariable(String name, String scopeName, Object value) { |
| JellyContext scope = getScope(scopeName); |
| if ( scope != null ) { |
| scope.setVariable(name, value); |
| } |
| } |
| |
| /** Removes the given variable */ |
| public void removeVariable(String name) { |
| variables.remove(name); |
| } |
| |
| /** |
| * Removes the given variable in the specified scope. |
| * |
| * @param name is the name of the variable |
| * @param scopeName is the optional scope name such as 'parent'. For servlet environments |
| * this could be 'application', 'session' or 'request'. |
| */ |
| public void removeVariable(String name, String scopeName) { |
| JellyContext scope = getScope(scopeName); |
| if ( scope != null ) { |
| scope.removeVariable(name); |
| } |
| } |
| |
| /** |
| * @return an Iterator over the current variable names in this |
| * context |
| */ |
| public Iterator getVariableNames() { |
| return variables.keySet().iterator(); |
| } |
| |
| /** |
| * @return the Map of variables in this scope |
| */ |
| public Map getVariables() { |
| return variables; |
| } |
| |
| /** |
| * Sets the Map of variables to use |
| */ |
| public void setVariables(Map variables) { |
| // I have seen this fail when the passed Map contains a key, value |
| // pair where the value is null |
| for (Iterator iter = variables.entrySet().iterator(); iter.hasNext();) { |
| Map.Entry element = (Map.Entry) iter.next(); |
| if (element.getValue() != null) { |
| this.variables.put(element.getKey(), element.getValue()); |
| } |
| } |
| //this.variables.putAll( variables ); |
| } |
| |
| /** |
| * A factory method to create a new child context of the |
| * current context. |
| */ |
| public JellyContext newJellyContext(Map newVariables) { |
| // XXXX: should allow this new context to |
| // XXXX: inherit parent contexts? |
| // XXXX: Or at least publish the parent scope |
| // XXXX: as a Map in this new variable scope? |
| newVariables.put("parentScope", variables); |
| JellyContext answer = createChildContext(); |
| answer.setVariables(newVariables); |
| return answer; |
| } |
| |
| /** |
| * A factory method to create a new child context of the |
| * current context. |
| */ |
| public JellyContext newJellyContext() { |
| return createChildContext(); |
| } |
| |
| |
| /** Gets the Script data item that may have previously been stored |
| * by the script, in this context, for the current thread. |
| * |
| * @return the tag associated with the current context and thread |
| */ |
| public Object getThreadScriptData(Script script) { |
| if( script == null ) |
| return null; |
| Tag tag = (Tag) getThreadScriptDataMap().get(script); |
| if( tag == null && getParent() != null) { |
| return getParent().getThreadScriptData(script); |
| } else { |
| return tag; |
| } |
| } |
| |
| /** Gets a per-thread (thread local) Map of data for use by |
| * Scripts. |
| * @return the thread local Map of Script data */ |
| public Map getThreadScriptDataMap() { |
| Map rv; |
| Map data = (Map) threadLocalScriptData.get(); |
| if (data == null) { |
| rv = new HashMap(); |
| threadLocalScriptData.set(rv); |
| } else { |
| rv = data; |
| } |
| return rv; |
| } |
| |
| /** Stores an object that lasts for the life of this context |
| * and is local to the current thread. This method is |
| * mainly intended to store Tag instances. However, any |
| * Script that wants to cache data can use this |
| * method. |
| */ |
| public void setThreadScriptData(Script script, Object data) { |
| getThreadScriptDataMap().put(script,data); |
| } |
| |
| /** Clears variables set by Tags (basically, variables set in a Jelly script) |
| * and data stored by {@link Script} instances. |
| * @see #clearVariables() |
| * @see #clearThreadScriptData() |
| * @see #clearScriptData() |
| */ |
| public void clear() { |
| getThreadScriptDataMap().clear(); |
| variables.clear(); |
| } |
| |
| /** Clears variables set by Tags (variables set while running a Jelly script) |
| * @see #clear() |
| * @see #clearThreadScriptData() |
| * @see #clearScriptData() |
| */ |
| public void clearVariables() { |
| variables.clear(); |
| } |
| |
| /** Clears data cached by {@link Script} instances, |
| * for this context, <strong>for the current thread</strong>. |
| * The data cleared could be cached Tag instances or other data |
| * saved by Script classes. |
| * @see #clear() |
| * @see #clearVariables() |
| * @see #clearScriptData() |
| */ |
| public void clearThreadScriptData() { |
| getThreadScriptDataMap().clear(); |
| } |
| |
| /** Clears data cached by {@link Script} instances, |
| * for this context, <strong>for all threads</strong>. |
| * The data cleared could be cached Tag instances or other data |
| * saved by Script classes. |
| * @see #clear() |
| * @see #clearThreadScriptData() |
| * @see #clearVariables() |
| */ |
| public void clearScriptData() { |
| threadLocalScriptData = new ThreadLocal(); |
| } |
| |
| /** Registers the given tag library against the given namespace URI. |
| * This should be called before the parser is used. |
| */ |
| public void registerTagLibrary(String namespaceURI, TagLibrary taglib) { |
| if (log.isDebugEnabled()) { |
| log.debug("Registering tag library to: " + namespaceURI + " taglib: " + taglib); |
| } |
| taglibs.put(namespaceURI, taglib); |
| |
| if (isExportLibraries() && parent != null) { |
| parent.registerTagLibrary( namespaceURI, taglib ); |
| } |
| } |
| |
| /** Registers the given tag library class name against the given namespace URI. |
| * The class will be loaded via the given ClassLoader |
| * This should be called before the parser is used. |
| */ |
| public void registerTagLibrary( |
| String namespaceURI, |
| String className) { |
| |
| if (log.isDebugEnabled()) { |
| log.debug("Registering tag library to: " + namespaceURI + " taglib: " + className); |
| } |
| taglibs.put(namespaceURI, className); |
| |
| if (isExportLibraries() && parent != null) { |
| parent.registerTagLibrary( namespaceURI, className ); |
| } |
| } |
| |
| public boolean isTagLibraryRegistered(String namespaceURI) { |
| boolean answer = taglibs.containsKey( namespaceURI ); |
| if (answer) { |
| return true; |
| } |
| else if ( parent != null ) { |
| return parent.isTagLibraryRegistered(namespaceURI); |
| } |
| else { |
| return false; |
| } |
| } |
| |
| /** |
| * @return the TagLibrary for the given namespace URI or null if one could not be found |
| */ |
| public TagLibrary getTagLibrary(String namespaceURI) { |
| |
| // use my own mapping first, so that namespaceURIs can |
| // be redefined inside child contexts... |
| |
| Object answer = taglibs.get(namespaceURI); |
| |
| if ( answer == null && parent != null ) { |
| answer = parent.getTagLibrary( namespaceURI ); |
| } |
| |
| if ( answer instanceof TagLibrary ) { |
| return (TagLibrary) answer; |
| } |
| else if ( answer instanceof String ) { |
| String className = (String) answer; |
| Class theClass = null; |
| try { |
| theClass = getClassLoader().loadClass(className); |
| } |
| catch (ClassNotFoundException e) { |
| log.error("Could not find the class: " + className, e); |
| } |
| if ( theClass != null ) { |
| try { |
| Object object = theClass.newInstance(); |
| if (object instanceof TagLibrary) { |
| taglibs.put(namespaceURI, object); |
| return (TagLibrary) object; |
| } |
| else { |
| log.error( |
| "The tag library object mapped to: " |
| + namespaceURI |
| + " is not a TagLibrary. Object = " |
| + object); |
| } |
| } |
| catch (Exception e) { |
| log.error( |
| "Could not instantiate instance of class: " + className + ". Reason: " + e, |
| e); |
| } |
| } |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Attempts to parse the script from the given uri using the |
| * {@link #getResource} method then returns the compiled script. |
| */ |
| public Script compileScript(String uri) throws JellyException { |
| XMLParser parser = getXMLParser(); |
| parser.setContext(this); |
| InputStream in = getResourceAsStream(uri); |
| if (in == null) { |
| throw new JellyException("Could not find Jelly script: " + uri); |
| } |
| Script script = null; |
| try { |
| script = parser.parse(in); |
| } catch (IOException e) { |
| throw new JellyException(JellyContext.BAD_PARSE, e); |
| } catch (SAXException e) { |
| throw new JellyException(JellyContext.BAD_PARSE, e); |
| } |
| |
| return script.compile(); |
| } |
| |
| /** |
| * Attempts to parse the script from the given URL using the |
| * {@link #getResource} method then returns the compiled script. |
| */ |
| public Script compileScript(URL url) throws JellyException { |
| XMLParser parser = getXMLParser(); |
| parser.setContext(this); |
| |
| Script script = null; |
| try { |
| script = parser.parse(url.toString()); |
| } catch (IOException e) { |
| throw new JellyException(JellyContext.BAD_PARSE, e); |
| } catch (SAXException e) { |
| throw new JellyException(JellyContext.BAD_PARSE, e); |
| } |
| |
| return script.compile(); |
| } |
| |
| /** |
| * Attempts to parse the script from the given InputSource using the |
| * {@link #getResource} method then returns the compiled script. |
| */ |
| public Script compileScript(InputSource source) throws JellyException { |
| XMLParser parser = getXMLParser(); |
| parser.setContext(this); |
| |
| Script script = null; |
| try { |
| script = parser.parse(source); |
| } catch (IOException e) { |
| throw new JellyException(JellyContext.BAD_PARSE, e); |
| } catch (SAXException e) { |
| throw new JellyException(JellyContext.BAD_PARSE, e); |
| } |
| |
| return script.compile(); |
| } |
| |
| /** |
| * @return a thread pooled XMLParser to avoid the startup overhead |
| * of the XMLParser |
| */ |
| protected XMLParser getXMLParser() { |
| XMLParser parser = createXMLParser(); |
| return parser; |
| } |
| |
| /** |
| * Factory method to allow JellyContext implementations to overload how an XMLParser |
| * is created - such as to overload what the default ExpressionFactory should be. |
| */ |
| protected XMLParser createXMLParser() { |
| return new XMLParser(); |
| } |
| |
| /** |
| * Parses the script from the given File then compiles it and runs it. |
| * |
| * @return the new child context that was used to run the script |
| */ |
| public JellyContext runScript(File file, XMLOutput output) throws JellyException { |
| try { |
| return runScript(file.toURL(), output, JellyContext.DEFAULT_EXPORT, |
| JellyContext.DEFAULT_INHERIT); |
| } catch (MalformedURLException e) { |
| throw new JellyException(e.toString()); |
| } |
| } |
| |
| /** |
| * Parses the script from the given URL then compiles it and runs it. |
| * |
| * @return the new child context that was used to run the script |
| */ |
| public JellyContext runScript(URL url, XMLOutput output) throws JellyException { |
| return runScript(url, output, JellyContext.DEFAULT_EXPORT, |
| JellyContext.DEFAULT_INHERIT); |
| } |
| |
| /** |
| * Parses the script from the given InputSource then compiles it and runs it. |
| * |
| * @return the new child context that was used to run the script |
| */ |
| public JellyContext runScript(InputSource source, XMLOutput output) throws JellyException { |
| return runScript(source, output, JellyContext.DEFAULT_EXPORT, |
| JellyContext.DEFAULT_INHERIT); |
| } |
| |
| /** |
| * Parses the script from the given uri using the |
| * JellyContext.getResource() API then compiles it and runs it. |
| * |
| * @return the new child context that was used to run the script |
| */ |
| public JellyContext runScript(String uri, XMLOutput output) throws JellyException { |
| URL url = null; |
| try { |
| url = getResource(uri); |
| } catch (MalformedURLException e) { |
| throw new JellyException(e.toString()); |
| } |
| |
| if (url == null) { |
| throw new JellyException("Could not find Jelly script: " + url); |
| } |
| return runScript(url, output, JellyContext.DEFAULT_EXPORT, |
| JellyContext.DEFAULT_INHERIT); |
| } |
| |
| /** |
| * Parses the script from the given uri using the |
| * JellyContext.getResource() API then compiles it and runs it. |
| * |
| * @return the new child context that was used to run the script |
| */ |
| public JellyContext runScript(String uri, XMLOutput output, |
| boolean export, boolean inherit) throws JellyException { |
| URL url = null; |
| try { |
| url = getResource(uri); |
| } catch (MalformedURLException e) { |
| throw new JellyException(e.toString()); |
| } |
| |
| if (url == null) { |
| throw new JellyException("Could not find Jelly script: " + url); |
| } |
| |
| return runScript(url, output, export, inherit); |
| } |
| |
| /** |
| * Parses the script from the given file then compiles it and runs it. |
| * |
| * @return the new child context that was used to run the script |
| */ |
| public JellyContext runScript(File file, XMLOutput output, |
| boolean export, boolean inherit) throws JellyException { |
| try { |
| return runScript(file.toURL(), output, export, inherit); |
| } catch (MalformedURLException e) { |
| throw new JellyException(e.toString()); |
| } |
| } |
| |
| /** |
| * Parses the script from the given URL then compiles it and runs it. |
| * |
| * @return the new child context that was used to run the script |
| */ |
| public JellyContext runScript(URL url, XMLOutput output, |
| boolean export, boolean inherit) throws JellyException { |
| return runScript(new InputSource(url.toString()), output, export, inherit); |
| } |
| |
| /** |
| * Parses the script from the given InputSource then compiles it and runs it. |
| * |
| * @return the new child context that was used to run the script |
| */ |
| public JellyContext runScript(InputSource source, XMLOutput output, |
| boolean export, boolean inherit) throws JellyException { |
| Script script = compileScript(source); |
| |
| URL newJellyContextURL = null; |
| try { |
| newJellyContextURL = getJellyContextURL(source); |
| } catch (MalformedURLException e) { |
| throw new JellyException(e.toString()); |
| } |
| |
| JellyContext newJellyContext = newJellyContext(); |
| newJellyContext.setRootURL( newJellyContextURL ); |
| newJellyContext.setCurrentURL( newJellyContextURL ); |
| newJellyContext.setExport( export ); |
| newJellyContext.setInherit( inherit ); |
| |
| if ( inherit ) { |
| // use the same variable scopes |
| newJellyContext.variables = this.variables; |
| } |
| |
| if (log.isDebugEnabled() ) { |
| log.debug( "About to run script: " + source.getSystemId() ); |
| log.debug( "root context URL: " + newJellyContext.rootURL ); |
| log.debug( "current context URL: " + newJellyContext.currentURL ); |
| } |
| |
| script.run(newJellyContext, output); |
| |
| return newJellyContext; |
| } |
| |
| /** |
| * Returns a URL for the given resource from the specified path. |
| * If the uri starts with "/" then the path is taken as relative to |
| * the current context root. |
| * If the uri is a well formed URL then it is used. |
| * If the uri is a file that exists and can be read then it is used. |
| * Otherwise the uri is interpreted as relative to the current context (the |
| * location of the current script). |
| */ |
| public URL getResource(String uri) throws MalformedURLException { |
| if (uri.startsWith("/")) { |
| // append this uri to the context root |
| return createRelativeURL(rootURL, uri.substring(1)); |
| } |
| else { |
| try { |
| return new URL(uri); |
| } |
| catch (MalformedURLException e) { |
| // lets try find a relative resource |
| try { |
| return createRelativeURL(currentURL, uri); |
| } catch (MalformedURLException e2) { |
| throw e; |
| } |
| } |
| } |
| } |
| |
| /** |
| * Attempts to open an InputStream to the given resource at the specified path. |
| * If the uri starts with "/" then the path is taken as relative to |
| * the current context root. If the uri is a well formed URL then it |
| * is used. Otherwise the uri is interpreted as relative to the current |
| * context (the location of the current script). |
| * |
| * @return null if this resource could not be loaded, otherwise the resources |
| * input stream is returned. |
| */ |
| public InputStream getResourceAsStream(String uri) { |
| try { |
| URL url = getResource(uri); |
| return url.openStream(); |
| } |
| catch (Exception e) { |
| if (log.isTraceEnabled()) { |
| log.trace( |
| "Caught exception attempting to open: " + uri + ". Exception: " + e, |
| e); |
| } |
| return null; |
| } |
| } |
| |
| |
| // Properties |
| //------------------------------------------------------------------------- |
| |
| /** |
| * @return the current root context URL from which all absolute resource URIs |
| * will be relative to. For example in a web application the root URL will |
| * map to the web directory which contains the WEB-INF directory. |
| */ |
| public URL getRootURL() { |
| return rootURL; |
| } |
| |
| /** |
| * Sets the current root context URL from which all absolute resource URIs |
| * will be relative to. For example in a web application the root URL will |
| * map to the web directory which contains the WEB-INF directory. |
| */ |
| public void setRootURL(URL rootURL) { |
| this.rootURL = rootURL; |
| } |
| |
| |
| /** |
| * @return the current URL context of the current script that is executing. |
| * This URL context is used to deduce relative scripts when relative URIs are |
| * used in calls to {@link #getResource} to process relative scripts. |
| */ |
| public URL getCurrentURL() { |
| return currentURL; |
| } |
| |
| /** |
| * Sets the current URL context of the current script that is executing. |
| * This URL context is used to deduce relative scripts when relative URIs are |
| * used in calls to {@link #getResource} to process relative scripts. |
| */ |
| public void setCurrentURL(URL currentURL) { |
| this.currentURL = currentURL; |
| } |
| |
| /** |
| * Returns whether we export tag libraries to our parents context |
| * @return boolean |
| */ |
| public boolean isExportLibraries() { |
| return exportLibraries; |
| } |
| |
| /** |
| * Sets whether we export tag libraries to our parents context |
| * @param exportLibraries The exportLibraries to set |
| */ |
| public void setExportLibraries(boolean exportLibraries) { |
| this.exportLibraries = exportLibraries; |
| } |
| |
| |
| /** |
| * Sets whether we should export variable definitions to our parent context |
| */ |
| public void setExport(boolean export) { |
| this.export = export; |
| } |
| |
| /** |
| * @return whether we should export variable definitions to our parent context |
| */ |
| public boolean isExport() { |
| return this.export; |
| } |
| |
| /** |
| * Sets whether we should inherit variables from our parent context |
| */ |
| public void setInherit(boolean inherit) { |
| this.inherit = inherit; |
| } |
| |
| /** |
| * @return whether we should inherit variables from our parent context |
| */ |
| public boolean isInherit() { |
| return this.inherit; |
| } |
| |
| |
| /** |
| * Return the class loader to be used for instantiating application objects |
| * when required. This is determined based upon the following rules: |
| * <ul> |
| * <li>The class loader set by <code>setClassLoader()</code>, if any</li> |
| * <li>The thread context class loader, if it exists and the |
| * <code>useContextClassLoader</code> property is set to true</li> |
| * <li>The class loader used to load the XMLParser class itself. |
| * </ul> |
| */ |
| public ClassLoader getClassLoader() { |
| return ClassLoaderUtils.getClassLoader(classLoader, useContextClassLoader, getClass()); |
| } |
| |
| /** |
| * Set the class loader to be used for instantiating application objects |
| * when required. |
| * |
| * @param classLoader The new class loader to use, or <code>null</code> |
| * to revert to the standard rules |
| */ |
| public void setClassLoader(ClassLoader classLoader) { |
| this.classLoader = classLoader; |
| } |
| |
| /** |
| * Return the boolean as to whether the context classloader should be used. |
| */ |
| public boolean getUseContextClassLoader() { |
| return useContextClassLoader; |
| } |
| |
| /** |
| * Determine whether to use the Context ClassLoader (the one found by |
| * calling <code>Thread.currentThread().getContextClassLoader()</code>) |
| * to resolve/load classes. If not |
| * using Context ClassLoader, then the class-loading defaults to |
| * using the calling-class' ClassLoader. |
| * |
| * @param use determines whether to use JellyContext ClassLoader. |
| */ |
| public void setUseContextClassLoader(boolean use) { |
| useContextClassLoader = use; |
| } |
| |
| |
| // Implementation methods |
| //------------------------------------------------------------------------- |
| /** |
| * @return a new relative URL from the given root and with the addition of the |
| * extra relative URI |
| * |
| * @param rootURL is the root context from which the relative URI will be applied |
| * @param relativeURI is the relative URI (without a leading "/") |
| * @throws MalformedURLException if the URL is invalid. |
| */ |
| protected URL createRelativeURL(URL rootURL, String relativeURI) |
| throws MalformedURLException { |
| URL url = rootURL; |
| if (url == null) { |
| File file = new File(System.getProperty("user.dir")); |
| url = file.toURL(); |
| } |
| String urlText = url.toString() + relativeURI; |
| if ( log.isDebugEnabled() ) { |
| log.debug("Attempting to open url: " + urlText); |
| } |
| return new URL(urlText); |
| } |
| |
| /** |
| * Strips off the name of a script to create a new context URL |
| */ |
| protected URL getJellyContextURL(URL url) throws MalformedURLException { |
| String text = url.toString(); |
| int idx = text.lastIndexOf('/'); |
| text = text.substring(0, idx + 1); |
| return new URL(text); |
| } |
| |
| /** |
| * Strips off the name of a script to create a new context URL |
| */ |
| protected URL getJellyContextURL(InputSource source) throws MalformedURLException { |
| String text = source.getSystemId(); |
| if (text != null) { |
| int idx = text.lastIndexOf('/'); |
| text = text.substring(0, idx + 1); |
| return new URL(text); |
| } else { |
| return null; |
| } |
| |
| } |
| |
| /** |
| * Factory method to create a new child of this context |
| */ |
| protected JellyContext createChildContext() { |
| return new JellyContext(this); |
| } |
| |
| /** |
| * Change the parent context to the one provided |
| * @param context the new parent context |
| */ |
| protected void setParent(JellyContext context) |
| { |
| parent = context; |
| this.variables.put("parentScope", parent.variables); |
| // need to re-export tag libraries to the new parent |
| if (isExportLibraries() && parent != null) { |
| for (Iterator keys = taglibs.keySet().iterator(); keys.hasNext();) |
| { |
| String namespaceURI = (String) keys.next(); |
| Object tagLibOrClassName = taglibs.get(namespaceURI); |
| if (tagLibOrClassName instanceof TagLibrary) |
| { |
| parent.registerTagLibrary( namespaceURI, (TagLibrary) tagLibOrClassName ); |
| } |
| else |
| { |
| parent.registerTagLibrary( namespaceURI, (String) tagLibOrClassName ); |
| } |
| } |
| } |
| |
| } |
| |
| } |