Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
TurbineVelocityService |
|
| 3.3125;3,312 |
1 | package org.apache.turbine.services.velocity; | |
2 | ||
3 | ||
4 | /* | |
5 | * Licensed to the Apache Software Foundation (ASF) under one | |
6 | * or more contributor license agreements. See the NOTICE file | |
7 | * distributed with this work for additional information | |
8 | * regarding copyright ownership. The ASF licenses this file | |
9 | * to you under the Apache License, Version 2.0 (the | |
10 | * "License"); you may not use this file except in compliance | |
11 | * with the License. You may obtain a copy of the License at | |
12 | * | |
13 | * http://www.apache.org/licenses/LICENSE-2.0 | |
14 | * | |
15 | * Unless required by applicable law or agreed to in writing, | |
16 | * software distributed under the License is distributed on an | |
17 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
18 | * KIND, either express or implied. See the License for the | |
19 | * specific language governing permissions and limitations | |
20 | * under the License. | |
21 | */ | |
22 | ||
23 | ||
24 | import java.io.ByteArrayOutputStream; | |
25 | import java.io.IOException; | |
26 | import java.io.OutputStream; | |
27 | import java.io.OutputStreamWriter; | |
28 | import java.io.Writer; | |
29 | import java.util.Iterator; | |
30 | import java.util.List; | |
31 | ||
32 | import org.apache.commons.collections.ExtendedProperties; | |
33 | import org.apache.commons.configuration.Configuration; | |
34 | import org.apache.commons.lang.StringUtils; | |
35 | import org.apache.commons.logging.Log; | |
36 | import org.apache.commons.logging.LogFactory; | |
37 | import org.apache.turbine.Turbine; | |
38 | import org.apache.turbine.pipeline.PipelineData; | |
39 | import org.apache.turbine.services.InitializationException; | |
40 | import org.apache.turbine.services.TurbineServices; | |
41 | import org.apache.turbine.services.pull.PullService; | |
42 | import org.apache.turbine.services.template.BaseTemplateEngineService; | |
43 | import org.apache.turbine.util.RunData; | |
44 | import org.apache.turbine.util.TurbineException; | |
45 | import org.apache.velocity.VelocityContext; | |
46 | import org.apache.velocity.app.VelocityEngine; | |
47 | import org.apache.velocity.app.event.EventCartridge; | |
48 | import org.apache.velocity.app.event.MethodExceptionEventHandler; | |
49 | import org.apache.velocity.context.Context; | |
50 | import org.apache.velocity.runtime.RuntimeConstants; | |
51 | import org.apache.velocity.runtime.log.CommonsLogLogChute; | |
52 | ||
53 | /** | |
54 | * This is a Service that can process Velocity templates from within a | |
55 | * Turbine Screen. It is used in conjunction with the templating service | |
56 | * as a Templating Engine for templates ending in "vm". It registers | |
57 | * itself as translation engine with the template service and gets | |
58 | * accessed from there. After configuring it in your properties, it | |
59 | * should never be necessary to call methods from this service directly. | |
60 | * | |
61 | * Here's an example of how you might use it from a | |
62 | * screen:<br> | |
63 | * | |
64 | * <code> | |
65 | * Context context = TurbineVelocity.getContext(data);<br> | |
66 | * context.put("message", "Hello from Turbine!");<br> | |
67 | * String results = TurbineVelocity.handleRequest(context,"helloWorld.vm");<br> | |
68 | * data.getPage().getBody().addElement(results);<br> | |
69 | * </code> | |
70 | * | |
71 | * @author <a href="mailto:mbryson@mont.mindspring.com">Dave Bryson</a> | |
72 | * @author <a href="mailto:krzewski@e-point.pl">Rafal Krzewski</a> | |
73 | * @author <a href="mailto:jvanzyl@periapt.com">Jason van Zyl</a> | |
74 | * @author <a href="mailto:sean@informage.ent">Sean Legassick</a> | |
75 | * @author <a href="mailto:dlr@finemaltcoding.com">Daniel Rall</a> | |
76 | * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a> | |
77 | * @author <a href="mailto:epugh@upstate.com">Eric Pugh</a> | |
78 | * @author <a href="mailto:peter@courcoux.biz">Peter Courcoux</a> | |
79 | * @version $Id: TurbineVelocityService.java 1773378 2016-12-09 13:19:59Z tv $ | |
80 | */ | |
81 | 24 | public class TurbineVelocityService |
82 | extends BaseTemplateEngineService | |
83 | implements VelocityService, | |
84 | MethodExceptionEventHandler | |
85 | { | |
86 | /** The generic resource loader path property in velocity.*/ | |
87 | private static final String RESOURCE_LOADER_PATH = ".resource.loader.path"; | |
88 | ||
89 | /** Default character set to use if not specified in the RunData object. */ | |
90 | private static final String DEFAULT_CHAR_SET = "ISO-8859-1"; | |
91 | ||
92 | /** The prefix used for URIs which are of type <code>jar</code>. */ | |
93 | private static final String JAR_PREFIX = "jar:"; | |
94 | ||
95 | /** The prefix used for URIs which are of type <code>absolute</code>. */ | |
96 | private static final String ABSOLUTE_PREFIX = "file://"; | |
97 | ||
98 | /** Logging */ | |
99 | 24 | private static final Log log = LogFactory.getLog(TurbineVelocityService.class); |
100 | ||
101 | /** Encoding used when reading the templates. */ | |
102 | private String defaultInputEncoding; | |
103 | ||
104 | /** Encoding used by the outputstream when handling the requests. */ | |
105 | private String defaultOutputEncoding; | |
106 | ||
107 | /** Is the pullModelActive? */ | |
108 | 24 | private boolean pullModelActive = false; |
109 | ||
110 | /** Shall we catch Velocity Errors and report them in the log file? */ | |
111 | 24 | private boolean catchErrors = true; |
112 | ||
113 | /** Velocity runtime instance */ | |
114 | 24 | private VelocityEngine velocity = null; |
115 | ||
116 | /** Internal Reference to the pull Service */ | |
117 | 24 | private PullService pullService = null; |
118 | ||
119 | ||
120 | /** | |
121 | * Load all configured components and initialize them. This is | |
122 | * a zero parameter variant which queries the Turbine Servlet | |
123 | * for its config. | |
124 | * | |
125 | * @throws InitializationException Something went wrong in the init | |
126 | * stage | |
127 | */ | |
128 | @Override | |
129 | public void init() | |
130 | throws InitializationException | |
131 | { | |
132 | try | |
133 | { | |
134 | 28 | initVelocity(); |
135 | ||
136 | // We can only load the Pull Model ToolBox | |
137 | // if the Pull service has been listed in the TR.props | |
138 | // and the service has successfully been initialized. | |
139 | 28 | if (TurbineServices.getInstance().isRegistered(PullService.SERVICE_NAME)) |
140 | { | |
141 | 28 | pullModelActive = true; |
142 | 28 | pullService = (PullService)TurbineServices.getInstance().getService(PullService.SERVICE_NAME); |
143 | ||
144 | 28 | log.debug("Activated Pull Tools"); |
145 | } | |
146 | ||
147 | // Register with the template service. | |
148 | 28 | registerConfiguration(VelocityService.VELOCITY_EXTENSION); |
149 | ||
150 | 28 | defaultInputEncoding = getConfiguration().getString("input.encoding", DEFAULT_CHAR_SET); |
151 | 28 | defaultOutputEncoding = getConfiguration().getString("output.encoding", defaultInputEncoding); |
152 | ||
153 | 28 | setInit(true); |
154 | } | |
155 | 0 | catch (Exception e) |
156 | { | |
157 | 0 | throw new InitializationException( |
158 | "Failed to initialize TurbineVelocityService", e); | |
159 | 28 | } |
160 | 28 | } |
161 | ||
162 | /** | |
163 | * Create a Context object that also contains the globalContext. | |
164 | * | |
165 | * @return A Context object. | |
166 | */ | |
167 | @Override | |
168 | public Context getContext() | |
169 | { | |
170 | 9 | Context globalContext = |
171 | pullModelActive ? pullService.getGlobalContext() : null; | |
172 | ||
173 | 9 | Context ctx = new VelocityContext(globalContext); |
174 | 9 | return ctx; |
175 | } | |
176 | ||
177 | /** | |
178 | * This method returns a new, empty Context object. | |
179 | * | |
180 | * @return A Context Object. | |
181 | */ | |
182 | @Override | |
183 | public Context getNewContext() | |
184 | { | |
185 | 28 | Context ctx = new VelocityContext(); |
186 | ||
187 | // Attach an Event Cartridge to it, so we get exceptions | |
188 | // while invoking methods from the Velocity Screens | |
189 | 28 | EventCartridge ec = new EventCartridge(); |
190 | 28 | ec.addEventHandler(this); |
191 | 28 | ec.attachToContext(ctx); |
192 | 28 | return ctx; |
193 | } | |
194 | ||
195 | /** | |
196 | * MethodException Event Cartridge handler | |
197 | * for Velocity. | |
198 | * | |
199 | * It logs an execption thrown by the velocity processing | |
200 | * on error level into the log file | |
201 | * | |
202 | * @param clazz The class that threw the exception | |
203 | * @param method The Method name that threw the exception | |
204 | * @param e The exception that would've been thrown | |
205 | * @return A valid value to be used as Return value | |
206 | * @throws Exception We threw the exception further up | |
207 | */ | |
208 | @Override | |
209 | @SuppressWarnings("rawtypes") // Interface not generified | |
210 | public Object methodException(Class clazz, String method, Exception e) | |
211 | throws Exception | |
212 | { | |
213 | 0 | log.error("Class " + clazz.getName() + "." + method + " threw Exception", e); |
214 | ||
215 | 0 | if (!catchErrors) |
216 | { | |
217 | 0 | throw e; |
218 | } | |
219 | ||
220 | 0 | return "[Turbine caught an Error here. Look into the turbine.log for further information]"; |
221 | } | |
222 | ||
223 | /** | |
224 | * Create a Context from the PipelineData object. Adds a pointer to | |
225 | * the PipelineData object to the VelocityContext so that PipelineData | |
226 | * is available in the templates. | |
227 | * | |
228 | * @param pipelineData The Turbine PipelineData object. | |
229 | * @return A clone of the WebContext needed by Velocity. | |
230 | */ | |
231 | @Override | |
232 | public Context getContext(PipelineData pipelineData) | |
233 | { | |
234 | //Map runDataMap = (Map)pipelineData.get(RunData.class); | |
235 | 28 | RunData data = (RunData)pipelineData; |
236 | // Attempt to get it from the data first. If it doesn't | |
237 | // exist, create it and then stuff it into the data. | |
238 | 25 | Context context = (Context) |
239 | data.getTemplateInfo().getTemplateContext(VelocityService.CONTEXT); | |
240 | ||
241 | 25 | if (context == null) |
242 | { | |
243 | 9 | context = getContext(); |
244 | 9 | context.put(VelocityService.RUNDATA_KEY, data); |
245 | // we will add both data and pipelineData to the context. | |
246 | 9 | context.put(VelocityService.PIPELINEDATA_KEY, pipelineData); |
247 | ||
248 | 9 | if (pullModelActive) |
249 | { | |
250 | // Populate the toolbox with request scope, session scope | |
251 | // and persistent scope tools (global tools are already in | |
252 | // the toolBoxContent which has been wrapped to construct | |
253 | // this request-specific context). | |
254 | 9 | pullService.populateContext(context, pipelineData); |
255 | } | |
256 | ||
257 | 9 | data.getTemplateInfo().setTemplateContext( |
258 | VelocityService.CONTEXT, context); | |
259 | } | |
260 | 25 | return context; |
261 | } | |
262 | ||
263 | /** | |
264 | * Process the request and fill in the template with the values | |
265 | * you set in the Context. | |
266 | * | |
267 | * @param context The populated context. | |
268 | * @param filename The file name of the template. | |
269 | * @return The process template as a String. | |
270 | * | |
271 | * @throws TurbineException Any exception thrown while processing will be | |
272 | * wrapped into a TurbineException and rethrown. | |
273 | */ | |
274 | @Override | |
275 | public String handleRequest(Context context, String filename) | |
276 | throws TurbineException | |
277 | { | |
278 | 2 | String results = null; |
279 | 2 | ByteArrayOutputStream bytes = null; |
280 | 2 | OutputStreamWriter writer = null; |
281 | 2 | String charset = getOutputCharSet(context); |
282 | ||
283 | try | |
284 | { | |
285 | 2 | bytes = new ByteArrayOutputStream(); |
286 | ||
287 | 2 | writer = new OutputStreamWriter(bytes, charset); |
288 | ||
289 | 2 | executeRequest(context, filename, writer); |
290 | 2 | writer.flush(); |
291 | 2 | results = bytes.toString(charset); |
292 | } | |
293 | 0 | catch (Exception e) |
294 | { | |
295 | 0 | renderingError(filename, e); |
296 | } | |
297 | finally | |
298 | { | |
299 | 0 | try |
300 | { | |
301 | 2 | if (bytes != null) |
302 | { | |
303 | 2 | bytes.close(); |
304 | } | |
305 | } | |
306 | 0 | catch (IOException ignored) |
307 | { | |
308 | // do nothing. | |
309 | 2 | } |
310 | 0 | } |
311 | 2 | return results; |
312 | } | |
313 | ||
314 | /** | |
315 | * Process the request and fill in the template with the values | |
316 | * you set in the Context. | |
317 | * | |
318 | * @param context A Context. | |
319 | * @param filename A String with the filename of the template. | |
320 | * @param output A OutputStream where we will write the process template as | |
321 | * a String. | |
322 | * | |
323 | * @throws TurbineException Any exception thrown while processing will be | |
324 | * wrapped into a TurbineException and rethrown. | |
325 | */ | |
326 | @Override | |
327 | public void handleRequest(Context context, String filename, | |
328 | OutputStream output) | |
329 | throws TurbineException | |
330 | { | |
331 | 2 | String charset = getOutputCharSet(context); |
332 | 2 | OutputStreamWriter writer = null; |
333 | ||
334 | try | |
335 | { | |
336 | 2 | writer = new OutputStreamWriter(output, charset); |
337 | 2 | executeRequest(context, filename, writer); |
338 | } | |
339 | 0 | catch (Exception e) |
340 | { | |
341 | 0 | renderingError(filename, e); |
342 | } | |
343 | finally | |
344 | { | |
345 | 0 | try |
346 | { | |
347 | 2 | if (writer != null) |
348 | { | |
349 | 2 | writer.flush(); |
350 | } | |
351 | } | |
352 | 0 | catch (Exception ignored) |
353 | { | |
354 | // do nothing. | |
355 | 2 | } |
356 | 0 | } |
357 | 2 | } |
358 | ||
359 | ||
360 | /** | |
361 | * Process the request and fill in the template with the values | |
362 | * you set in the Context. | |
363 | * | |
364 | * @param context A Context. | |
365 | * @param filename A String with the filename of the template. | |
366 | * @param writer A Writer where we will write the process template as | |
367 | * a String. | |
368 | * | |
369 | * @throws TurbineException Any exception thrown while processing will be | |
370 | * wrapped into a TurbineException and rethrown. | |
371 | */ | |
372 | @Override | |
373 | public void handleRequest(Context context, String filename, Writer writer) | |
374 | throws TurbineException | |
375 | { | |
376 | try | |
377 | { | |
378 | 0 | executeRequest(context, filename, writer); |
379 | } | |
380 | 0 | catch (Exception e) |
381 | { | |
382 | 0 | renderingError(filename, e); |
383 | } | |
384 | finally | |
385 | { | |
386 | 0 | try |
387 | { | |
388 | 0 | if (writer != null) |
389 | { | |
390 | 0 | writer.flush(); |
391 | } | |
392 | } | |
393 | 0 | catch (Exception ignored) |
394 | { | |
395 | // do nothing. | |
396 | 0 | } |
397 | 0 | } |
398 | 0 | } |
399 | ||
400 | ||
401 | /** | |
402 | * Process the request and fill in the template with the values | |
403 | * you set in the Context. Apply the character and template | |
404 | * encodings from RunData to the result. | |
405 | * | |
406 | * @param context A Context. | |
407 | * @param filename A String with the filename of the template. | |
408 | * @param writer A OutputStream where we will write the process template as | |
409 | * a String. | |
410 | * | |
411 | * @throws Exception A problem occurred. | |
412 | */ | |
413 | private void executeRequest(Context context, String filename, | |
414 | Writer writer) | |
415 | throws Exception | |
416 | { | |
417 | 4 | String encoding = getTemplateEncoding(context); |
418 | ||
419 | 4 | if (encoding == null) |
420 | { | |
421 | 0 | encoding = defaultOutputEncoding; |
422 | } | |
423 | ||
424 | 4 | velocity.mergeTemplate(filename, encoding, context, writer); |
425 | 4 | } |
426 | ||
427 | /** | |
428 | * Retrieve the required charset from the Turbine RunData in the context | |
429 | * | |
430 | * @param context A Context. | |
431 | * @return The character set applied to the resulting String. | |
432 | */ | |
433 | private String getOutputCharSet(Context context) | |
434 | { | |
435 | 4 | String charset = null; |
436 | ||
437 | 4 | Object data = context.get(VelocityService.RUNDATA_KEY); |
438 | 4 | if ((data != null) && (data instanceof RunData)) |
439 | { | |
440 | 4 | charset = ((RunData) data).getCharSet(); |
441 | } | |
442 | ||
443 | 4 | return (StringUtils.isEmpty(charset)) ? defaultOutputEncoding : charset; |
444 | } | |
445 | ||
446 | /** | |
447 | * Retrieve the required encoding from the Turbine RunData in the context | |
448 | * | |
449 | * @param context A Context. | |
450 | * @return The encoding applied to the resulting String. | |
451 | */ | |
452 | private String getTemplateEncoding(Context context) | |
453 | { | |
454 | 4 | String encoding = null; |
455 | ||
456 | 4 | Object data = context.get(VelocityService.RUNDATA_KEY); |
457 | 4 | if ((data != null) && (data instanceof RunData)) |
458 | { | |
459 | 4 | encoding = ((RunData) data).getTemplateEncoding(); |
460 | } | |
461 | ||
462 | 4 | return encoding != null ? encoding : defaultInputEncoding; |
463 | } | |
464 | ||
465 | /** | |
466 | * Macro to handle rendering errors. | |
467 | * | |
468 | * @param filename The file name of the unrenderable template. | |
469 | * @param e The error. | |
470 | * | |
471 | * @throws TurbineException Thrown every time. Adds additional | |
472 | * information to <code>e</code>. | |
473 | */ | |
474 | private static final void renderingError(String filename, Exception e) | |
475 | throws TurbineException | |
476 | { | |
477 | 0 | String err = "Error rendering Velocity template: " + filename; |
478 | 0 | log.error(err, e); |
479 | 0 | throw new TurbineException(err, e); |
480 | } | |
481 | ||
482 | /** | |
483 | * Setup the velocity runtime by using a subset of the | |
484 | * Turbine configuration which relates to velocity. | |
485 | * | |
486 | * @throws Exception An Error occurred. | |
487 | */ | |
488 | private synchronized void initVelocity() | |
489 | throws Exception | |
490 | { | |
491 | // Get the configuration for this service. | |
492 | 28 | Configuration conf = getConfiguration(); |
493 | ||
494 | 28 | catchErrors = conf.getBoolean(CATCH_ERRORS_KEY, CATCH_ERRORS_DEFAULT); |
495 | ||
496 | 28 | conf.setProperty(RuntimeConstants.RUNTIME_LOG_LOGSYSTEM_CLASS, |
497 | CommonsLogLogChute.class.getName()); | |
498 | 28 | conf.setProperty(CommonsLogLogChute.LOGCHUTE_COMMONS_LOG_NAME, |
499 | "velocity"); | |
500 | ||
501 | 28 | velocity = new VelocityEngine(); |
502 | 28 | velocity.setExtendedProperties(createVelocityProperties(conf)); |
503 | 28 | velocity.init(); |
504 | 28 | } |
505 | ||
506 | ||
507 | /** | |
508 | * This method generates the Extended Properties object necessary | |
509 | * for the initialization of Velocity. It also converts the various | |
510 | * resource loader pathes into webapp relative pathes. It also | |
511 | * | |
512 | * @param conf The Velocity Service configuration | |
513 | * | |
514 | * @return An ExtendedProperties Object for Velocity | |
515 | * | |
516 | * @throws Exception If a problem occurred while converting the properties. | |
517 | */ | |
518 | ||
519 | public ExtendedProperties createVelocityProperties(Configuration conf) | |
520 | throws Exception | |
521 | { | |
522 | // This bugger is public, because we want to run some Unit tests | |
523 | // on it. | |
524 | ||
525 | 29 | ExtendedProperties veloConfig = new ExtendedProperties(); |
526 | ||
527 | // Fix up all the template resource loader pathes to be | |
528 | // webapp relative. Copy all other keys verbatim into the | |
529 | // veloConfiguration. | |
530 | ||
531 | 29 | for (Iterator<String> i = conf.getKeys(); i.hasNext();) |
532 | { | |
533 | 544 | String key = i.next(); |
534 | 544 | if (!key.endsWith(RESOURCE_LOADER_PATH)) |
535 | { | |
536 | 435 | Object value = conf.getProperty(key); |
537 | 435 | if (value instanceof List<?>) { |
538 | 0 | for (Iterator<?> itr = ((List<?>)value).iterator(); itr.hasNext();) |
539 | { | |
540 | 0 | veloConfig.addProperty(key, itr.next()); |
541 | } | |
542 | } | |
543 | else | |
544 | { | |
545 | 435 | veloConfig.addProperty(key, value); |
546 | } | |
547 | 435 | continue; // for() |
548 | } | |
549 | ||
550 | 109 | List<Object> paths = conf.getList(key, null); |
551 | 109 | if (paths == null) |
552 | { | |
553 | // We don't copy this into VeloProperties, because | |
554 | // null value is unhealthy for the ExtendedProperties object... | |
555 | 0 | continue; // for() |
556 | } | |
557 | ||
558 | // Translate the supplied pathes given here. | |
559 | // the following three different kinds of | |
560 | // pathes must be translated to be webapp-relative | |
561 | // | |
562 | // jar:file://path-component!/entry-component | |
563 | // file://path-component | |
564 | // path/component | |
565 | 109 | for (Object p : paths) |
566 | { | |
567 | 109 | String path = (String)p; |
568 | 109 | log.debug("Translating " + path); |
569 | ||
570 | 109 | if (path.startsWith(JAR_PREFIX)) |
571 | { | |
572 | // skip jar: -> 4 chars | |
573 | 40 | if (path.substring(4).startsWith(ABSOLUTE_PREFIX)) |
574 | { | |
575 | // We must convert up to the jar path separator | |
576 | 30 | int jarSepIndex = path.indexOf("!/"); |
577 | ||
578 | // jar:file:// -> skip 11 chars | |
579 | 30 | path = (jarSepIndex < 0) |
580 | ? Turbine.getRealPath(path.substring(11)) | |
581 | // Add the path after the jar path separator again to the new url. | |
582 | : (Turbine.getRealPath(path.substring(11, jarSepIndex)) + path.substring(jarSepIndex)); | |
583 | ||
584 | 30 | log.debug("Result (absolute jar path): " + path); |
585 | 30 | } |
586 | } | |
587 | 69 | else if(path.startsWith(ABSOLUTE_PREFIX)) |
588 | { | |
589 | // skip file:// -> 7 chars | |
590 | 10 | path = Turbine.getRealPath(path.substring(7)); |
591 | ||
592 | 10 | log.debug("Result (absolute URL Path): " + path); |
593 | } | |
594 | // Test if this might be some sort of URL that we haven't encountered yet. | |
595 | 59 | else if(path.indexOf("://") < 0) |
596 | { | |
597 | 49 | path = Turbine.getRealPath(path); |
598 | ||
599 | 49 | log.debug("Result (normal fs reference): " + path); |
600 | } | |
601 | ||
602 | 109 | log.debug("Adding " + key + " -> " + path); |
603 | // Re-Add this property to the configuration object | |
604 | 109 | veloConfig.addProperty(key, path); |
605 | 109 | } |
606 | 109 | } |
607 | 29 | return veloConfig; |
608 | } | |
609 | ||
610 | /** | |
611 | * Find out if a given template exists. Velocity | |
612 | * will do its own searching to determine whether | |
613 | * a template exists or not. | |
614 | * | |
615 | * @param template String template to search for | |
616 | * @return True if the template can be loaded by Velocity | |
617 | */ | |
618 | @Override | |
619 | public boolean templateExists(String template) | |
620 | { | |
621 | 37 | return velocity.resourceExists(template); |
622 | } | |
623 | ||
624 | /** | |
625 | * Performs post-request actions (releases context | |
626 | * tools back to the object pool). | |
627 | * | |
628 | * @param context a Velocity Context | |
629 | */ | |
630 | @Override | |
631 | public void requestFinished(Context context) | |
632 | { | |
633 | 2 | if (pullModelActive) |
634 | { | |
635 | 2 | pullService.releaseTools(context); |
636 | } | |
637 | 2 | } |
638 | } |