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