View Javadoc

1   package org.apache.turbine.services.pull;
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.util.ArrayList;
25  import java.util.Iterator;
26  import java.util.List;
27  
28  import org.apache.commons.configuration.Configuration;
29  import org.apache.commons.logging.Log;
30  import org.apache.commons.logging.LogFactory;
31  import org.apache.fulcrum.pool.PoolService;
32  import org.apache.fulcrum.security.model.turbine.TurbineUserManager;
33  import org.apache.turbine.Turbine;
34  import org.apache.turbine.om.security.User;
35  import org.apache.turbine.pipeline.PipelineData;
36  import org.apache.turbine.services.InitializationException;
37  import org.apache.turbine.services.TurbineBaseService;
38  import org.apache.turbine.services.TurbineServices;
39  import org.apache.turbine.services.velocity.TurbineVelocity;
40  import org.apache.turbine.services.velocity.VelocityService;
41  import org.apache.turbine.util.RunData;
42  import org.apache.velocity.context.Context;
43  
44  /**
45   * This is the concrete implementation of the Turbine
46   * Pull Service.
47   * <p>
48   * These are tools that are placed in the context by the service
49   * These tools will be made available to all your
50   * templates. You list the tools in the following way:
51   * <p>
52   * <pre>
53   * tool.&lt;scope&gt;.&lt;id&gt; = &lt;classname&gt;
54   *
55   * &lt;scope&gt;      is the tool scope: global, request, session,
56   *              authorized or persistent (see below for more details)
57   * &lt;id&gt;         is the name of the tool in the context
58   *
59   * You can configure the tools in this way:
60   * tool.&lt;id&gt;.&lt;parameter&gt; = &lt;value&gt;
61   *
62   * So if you find "global", "request", "session" or "persistent" as second
63   * part, it is a configuration to put a tool into the toolbox, else it is a
64   * tool specific configuration.
65   *
66   * For example:
67   *
68   * tool.global.ui    = org.apache.turbine.util.pull.UIManager
69   * tool.global.mm    = org.apache.turbine.util.pull.MessageManager
70   * tool.request.link = org.apache.turbine.services.pull.tools.TemplateLink
71   * tool.request.page = org.apache.turbine.util.template.TemplatePageAttributes
72   *
73   * Then:
74   *
75   * tool.ui.skin = default
76   *
77   * configures the value of "skin" for the "ui" tool.
78   *
79   * Tools are accessible in all templates by the <id> given
80   * to the tool. So for the above listings the UIManager would
81   * be available as $ui, the MessageManager as $mm, the TemplateLink
82   * as $link and the TemplatePageAttributes as $page.
83   *
84   * You should avoid using tool names called "global", "request",
85   * "session" or "persistent" because of clashes with the possible Scopes.
86   *
87   * Scopes:
88   *
89   *  global:     tool is instantiated once and that instance is available
90   *              to all templates for all requests. Tool must be threadsafe.
91   *
92   *  request:    tool is instantiated once for each request (although the
93   *              PoolService is used to recycle instances). Tool need not
94   *              be threadsafe.
95   *
96   *  session:    tool is instantiated once for each user session, and is
97   *              stored in the session.  These tools do not need to be
98   *              threadsafe.
99   *
100  *  authorized: tool is instantiated once for each user session once the
101  *              user logs in. After this, it is a normal session tool.
102  *
103  *  persistent: tool is instantitated once for each user session once
104  *              the user logs in and is is stored in the user's permanent
105  *              hashtable.
106  *              This means for a logged in user the tool will be persisted
107  *              in the user's objectdata. Tool should be Serializable.  These
108  *              tools do not need to be threadsafe.
109  *              <b>persistent scope tools are deprecated in 2.3</b>
110  *
111  * Defaults: none
112  * </pre>
113  *
114  * @author <a href="mailto:jvanzyl@periapt.com">Jason van Zyl</a>
115  * @author <a href="mailto:sean@informage.net">Sean Legassick</a>
116  * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
117  * @author <a href="mailto:quintonm@bellsouth.net">Quinton McCombs</a>
118  * @author <a href="mailto:peter@courcoux.biz">Peter Courcoux</a>
119  * @version $Id: TurbinePullService.java 1706239 2015-10-01 13:18:35Z tv $
120  */
121 public class TurbinePullService
122         extends TurbineBaseService
123         implements PullService
124 {
125     /** Logging */
126     private static Log log = LogFactory.getLog(TurbinePullService.class);
127 
128     /** Reference to the pool service */
129     private PoolService pool = null;
130 
131     /** Reference to the templating (nee Velocity) service */
132     private VelocityService velocity = null;
133 
134     /**
135      * This is the container for the global web application
136      * tools that are used in conjunction with the
137      * Turbine Pull Model. All the global tools will be placed
138      * in this Context and be made accessible inside
139      * templates via the tool name specified in the TR.props
140      * file.
141      */
142     private Context globalContext;
143 
144     /**
145      * This inner class is used in the lists below to store the
146      * tool name and class for each of request, session and persistent
147      * tools
148      */
149     private static class ToolData
150     {
151         String toolName;
152         String toolClassName;
153         Class<ApplicationTool> toolClass;
154 
155         public ToolData(String toolName, String toolClassName, Class<ApplicationTool> toolClass)
156         {
157             this.toolName = toolName;
158             this.toolClassName = toolClassName;
159             this.toolClass = toolClass;
160         }
161     }
162 
163     /** Internal list of global tools */
164     private List<ToolData> globalTools;
165 
166     /** Internal list of request tools */
167     private List<ToolData> requestTools;
168 
169     /** Internal list of session tools */
170     private List<ToolData> sessionTools;
171 
172     /** Internal list of authorized tools */
173     private List<ToolData> authorizedTools;
174 
175     /** Internal list of persistent tools */
176     private List<ToolData> persistentTools;
177 
178     /** Directory where application tool resources are stored.*/
179     private String resourcesDirectory;
180 
181     /** Should we refresh the application tools on a per request basis? */
182     private boolean refreshToolsPerRequest = false;
183 
184     /**
185      * Called the first time the Service is used.
186      */
187     @Override
188     public void init()
189         throws InitializationException
190     {
191         try
192         {
193 		    pool = (PoolService)TurbineServices.getInstance().getService(PoolService.ROLE);
194 
195             if (pool == null)
196             {
197                 throw new InitializationException("Pull Service requires"
198                     + " configured Pool Service!");
199             }
200 
201             initPullService();
202             // Make sure to setInit(true) because Tools may
203             // make calls back to the TurbinePull static methods
204             // which causes an init loop.
205             setInit(true);
206 
207             // Do _NOT_ move this before the setInit(true)
208             velocity = TurbineVelocity.getService();
209 
210             if (velocity != null)
211             {
212                 initPullTools();
213             }
214             else
215             {
216                 log.info("Velocity Service not configured, skipping pull tools!");
217             }
218         }
219         catch (Exception e)
220         {
221             throw new InitializationException(
222                 "TurbinePullService failed to initialize", e);
223         }
224     }
225 
226     /**
227      * Initialize the pull service
228      *
229      * @exception Exception A problem happened when starting up
230      */
231     private void initPullService()
232         throws Exception
233     {
234         // This is the per-service configuration, prefixed with services.PullService
235         Configuration conf = getConfiguration();
236 
237         // Get the resources directory that is specificed
238         // in the TR.props or default to "resources", relative to the webapp.
239         resourcesDirectory = conf.getString(
240             TOOL_RESOURCES_DIR_KEY,
241             TOOL_RESOURCES_DIR_DEFAULT);
242 
243         // Should we refresh the tool box on a per
244         // request basis.
245         refreshToolsPerRequest =
246             conf.getBoolean(
247                 TOOLS_PER_REQUEST_REFRESH_KEY,
248                 TOOLS_PER_REQUEST_REFRESH_DEFAULT);
249 
250         // Log the fact that the application tool box will
251         // be refreshed on a per request basis.
252         if (refreshToolsPerRequest)
253         {
254             log.info("Pull Model tools will "
255                 + "be refreshed on a per request basis.");
256         }
257     }
258 
259     /**
260      * Initialize the pull tools. At this point, the
261      * service must be marked as initialized, because the
262      * tools may call the methods of this service via the
263      * static facade class TurbinePull.
264      *
265      * @exception Exception A problem happened when starting up
266      */
267     private void initPullTools()
268         throws Exception
269     {
270         // And for reasons I never really fully understood,
271         // the tools directive is toplevel without the service
272         // prefix. This is brain-damaged but for legacy reasons we
273         // keep this. So this is the global turbine configuration:
274         Configuration conf = Turbine.getConfiguration();
275 
276         // Grab each list of tools that are to be used (for global scope,
277         // request scope, authorized scope, session scope and persistent
278         // scope tools). They are specified respectively in the TR.props
279         // like this:
280         //
281         // tool.global.ui = org.apache.turbine.util.pull.UIManager
282         // tool.global.mm = org.apache.turbine.util.pull.MessageManager
283         //
284         // tool.request.link = org.apache.turbine.services.pull.tools.TemplateLink
285         //
286         // tool.session.basket = org.sample.util.ShoppingBasket;
287         //
288         // tool.persistent.ui = org.apache.turbine.services.pull.util.PersistentUIManager
289 
290         log.debug("Global Tools:");
291         globalTools     = getTools(conf.subset(GLOBAL_TOOL));
292         log.debug("Request Tools:");
293         requestTools    = getTools(conf.subset(REQUEST_TOOL));
294         log.debug("Session Tools:");
295         sessionTools    = getTools(conf.subset(SESSION_TOOL));
296         log.debug("Authorized Tools:");
297         authorizedTools = getTools(conf.subset(AUTHORIZED_TOOL));
298         log.debug("Persistent Tools:");
299         persistentTools = getTools(conf.subset(PERSISTENT_TOOL));
300 
301         // Create and populate the global context right now
302 
303         // This is unholy, because it entwines the VelocityService and
304         // the Pull Service even further. However, there isn't much we can
305         // do for the 2.3 release. Expect this to go post-2.3
306         globalContext = velocity.getNewContext();
307 
308         populateWithGlobalTools(globalContext);
309     }
310 
311     /**
312      * Retrieve the tool names and classes for the tools definied
313      * in the configuration file with the prefix given.
314      *
315      * @param toolConfig The part of the configuration describing some tools
316      */
317     @SuppressWarnings("unchecked")
318     private List<ToolData> getTools(Configuration toolConfig)
319     {
320         List<ToolData> tools = new ArrayList<ToolData>();
321 
322         // There might not be any tools for this prefix
323         // so return an empty list.
324         if (toolConfig == null)
325         {
326             return tools;
327         }
328 
329         for (Iterator<String> it = toolConfig.getKeys(); it.hasNext();)
330         {
331             String toolName = it.next();
332             String toolClassName = toolConfig.getString(toolName);
333 
334             try
335             {
336                 // Create an instance of the tool class.
337                 Class<ApplicationTool> toolClass = (Class<ApplicationTool>) Class.forName(toolClassName);
338 
339                 // Add the tool to the list being built.
340                 tools.add(new ToolData(toolName, toolClassName, toolClass));
341 
342                 log.info("Tool " + toolClassName
343                     + " to add to the context as '$" + toolName + "'");
344             }
345             catch (Exception e)
346             {
347                 log.error("Cannot instantiate tool class "
348                     + toolClassName + ": ", e);
349             }
350         }
351 
352         return tools;
353     }
354 
355     /**
356      * Return the Context which contains all global tools that
357      * are to be used in conjunction with the Turbine
358      * Pull Model. The tools are refreshed every time the
359      * global Context is pulled.
360      */
361     @Override
362     public Context getGlobalContext()
363     {
364         if (refreshToolsPerRequest)
365         {
366             refreshGlobalTools();
367         }
368         return globalContext;
369     }
370 
371     /**
372      * Populate the given context with all request, session, authorized
373      * and persistent scope tools (it is assumed that the context
374      * already wraps the global context, and thus already contains
375      * the global tools).
376      *
377      * @param context a Velocity Context to populate
378      * @param data a RunData object for request specific data
379      */
380     @Override
381     public void populateContext(Context context, RunData data)
382     {
383         populateWithRequestTools(context, data);
384 
385         // session tools (whether session-only or persistent are
386         // very similar, so the same method is used - the
387         // boolean parameter indicates whether get/setPerm is to be used
388         // rather than get/setTemp)
389 
390         //
391         // Session Tool start right at the session once the user has been set
392         // while persistent and authorized Tools are started when the user has
393         // logged in
394         //
395         User user = data.getUser();
396 
397         // Note: Session tools are currently lost after the login action
398         // because the anonymous user is replaced the the real user object.
399         // We should either store the session pull tools in the session or
400         // make Turbine.loginAction() copy the session pull tools into the
401         // new user object.
402         populateWithSessionTools(sessionTools, context, data, user);
403 
404         TurbineUserManager userManager =
405         	(TurbineUserManager)TurbineServices
406         		.getInstance()
407         		.getService(TurbineUserManager.ROLE);
408 
409         if (!userManager.isAnonymousUser(user))
410         {
411             if (user.hasLoggedIn())
412             {
413                 populateWithSessionTools(authorizedTools, context, data, user);
414                 populateWithPermTools(persistentTools, context, data, user);
415             }
416         }
417     }
418 
419     /**
420      * Populate the given context with all request, session, authorized
421      * and persistent scope tools (it is assumed that the context
422      * already wraps the global context, and thus already contains
423      * the global tools).
424      *
425      * @param context a Velocity Context to populate
426      * @param pipelineData a PipelineData object for request specific data
427      */
428     @Override
429     public void populateContext(Context context, PipelineData pipelineData)
430     {
431        // Map runDataMap = (Map) pipelineData.get(RunData.class);
432        // RunData data = (RunData)runDataMap.get(RunData.class);
433         RunData data = (RunData)pipelineData;
434 
435         populateWithRequestTools(context, pipelineData);
436         // session tools (whether session-only or persistent are
437         // very similar, so the same method is used - the
438         // boolean parameter indicates whether get/setPerm is to be used
439         // rather than get/setTemp)
440 
441         //
442         // Session Tool start right at the session once the user has been set
443         // while persistent and authorized Tools are started when the user has
444         // logged in
445         //
446         User user = data.getUser();
447 
448         // Note: Session tools are currently lost after the login action
449         // because the anonymous user is replaced the the real user object.
450         // We should either store the session pull tools in the session or
451         // make Turbine.loginAction() copy the session pull tools into the
452         // new user object.
453         populateWithSessionTools(sessionTools, context, pipelineData, user);
454 
455         TurbineUserManager userManager =
456         	(TurbineUserManager)TurbineServices
457         		.getInstance()
458         		.getService(TurbineUserManager.ROLE);
459 
460         if (!userManager.isAnonymousUser(user))
461         {
462             if (user.hasLoggedIn())
463             {
464                 populateWithSessionTools(authorizedTools, context, pipelineData, user);
465                 populateWithPermTools(persistentTools, context, pipelineData, user);
466             }
467         }
468     }
469 
470     /**
471      * Populate the given context with the global tools
472      *
473      * @param context a Velocity Context to populate
474      */
475     private void populateWithGlobalTools(Context context)
476     {
477         for (Iterator<ToolData> it = globalTools.iterator(); it.hasNext();)
478         {
479             ToolData toolData = it.next();
480             try
481             {
482                 Object tool = toolData.toolClass.newInstance();
483 
484                 // global tools are init'd with a null data parameter
485                 initTool(tool, null);
486 
487                 // put the tool in the context
488                 context.put(toolData.toolName, tool);
489             }
490             catch (Exception e)
491             {
492                 log.error("Could not instantiate global tool "
493                     + toolData.toolName + " from a "
494                     + toolData.toolClassName + " object", e);
495             }
496         }
497     }
498 
499     /**
500      * Populate the given context with the request-scope tools
501      *
502      * @param context a Velocity Context to populate
503      * @param pipelineData a RunData instance
504      */
505     private void populateWithRequestTools(Context context, RunData data)
506     {
507         // Iterate the tools
508         for (Iterator<ToolData> it = requestTools.iterator(); it.hasNext();)
509         {
510             ToolData toolData = it.next();
511             try
512             {
513                 // Fetch Object through the Pool.
514                 Object tool = pool.getInstance(toolData.toolClass);
515 
516                 // request tools are init'd with a RunData object
517                 initTool(tool, data);
518 
519                 // put the tool in the context
520                 context.put(toolData.toolName, tool);
521             }
522             catch (Exception e)
523             {
524                 log.error("Could not instantiate request tool "
525                     + toolData.toolName + " from a "
526                     + toolData.toolClassName + " object", e);
527             }
528         }
529     }
530 
531 
532     /**
533      * Populate the given context with the request-scope tools
534      *
535      * @param context a Velocity Context to populate
536      * @param pipelineData a RunData instance
537      */
538     private void populateWithRequestTools(Context context, PipelineData pipelineData)
539     {
540         // Iterate the tools
541         for (Iterator<ToolData> it = requestTools.iterator(); it.hasNext();)
542         {
543             ToolData toolData = it.next();
544             try
545             {
546                 // Fetch Object through the Pool.
547                 Object tool = pool.getInstance(toolData.toolClass);
548 
549                 initTool(tool, pipelineData);
550 
551                 // put the tool in the context
552                 context.put(toolData.toolName, tool);
553             }
554             catch (Exception e)
555             {
556                 log.error("Could not instantiate request tool "
557                     + toolData.toolName + " from a "
558                     + toolData.toolClassName + " object", e);
559             }
560         }
561     }
562 
563     /**
564      * Populate the given context with the session-scoped tools.
565      *
566      * @param tools The list of tools with which to populate the
567      * session.
568      * @param context The context to populate.
569      * @param pipelineData The current RunData object
570      * @param user The <code>User</code> object whose storage to
571      * retrieve the tool from.
572      */
573     private void populateWithSessionTools(List<ToolData> tools, Context context,
574             PipelineData pipelineData, User user)
575     {
576         //Map runDataMap = (Map)pipelineData.get(RunData.class);
577         //RunData data = (RunData) runDataMap.get(RunData.class);
578         RunData runData = (RunData)pipelineData;
579         // Iterate the tools
580         for (Iterator<ToolData> it = tools.iterator(); it.hasNext();)
581         {
582             ToolData toolData = it.next();
583             try
584             {
585                 // ensure that tool is created only once for a user
586                 // by synchronizing against the user object
587                 synchronized (runData.getSession())
588                 {
589                     // first try and fetch the tool from the user's
590                     // hashtable
591                     Object tool = runData.getSession().getAttribute(
592                             SESSION_TOOLS_ATTRIBUTE_PREFIX
593                             + toolData.toolClassName);
594 
595                     if (tool == null)
596                     {
597                         // if not there, an instance must be fetched from
598                         // the pool
599                         tool = pool.getInstance(toolData.toolClass);
600 
601                         // session tools are init'd with the User object
602                         initTool(tool, user);
603                     }
604 
605                     // *NOT* else
606                     if(tool != null)
607                     {
608                         // store the newly created tool in the session
609                         runData.getSession().setAttribute(
610                                 SESSION_TOOLS_ATTRIBUTE_PREFIX
611                                 + tool.getClass().getName(), tool);
612 
613                         // This is a semantics change. In the old
614                         // Turbine, Session tools were initialized and
615                         // then refreshed every time they were pulled
616                         // into the context if "refreshToolsPerRequest"
617                         // was wanted.
618                         //
619                         // RunDataApplicationTools now have a parameter
620                         // for refresh. If it is not refreshed immediately
621                         // after init(), the parameter value will be undefined
622                         // until the 2nd run. So we refresh all the session
623                         // tools on every run, even if we just init'ed it.
624                         //
625 
626                         if (refreshToolsPerRequest)
627                         {
628                             refreshTool(tool, pipelineData);
629                         }
630 
631                         // put the tool in the context
632                         log.debug("Adding " + tool + " to ctx as "
633                                 + toolData.toolName);
634                         context.put(toolData.toolName, tool);
635                     }
636                     else
637                     {
638                         log.info("Tool " + toolData.toolName
639                                 + " was null, skipping it.");
640                     }
641                 }
642             }
643             catch (Exception e)
644             {
645                 log.error("Could not instantiate session tool "
646                     + toolData.toolName + " from a "
647                     + toolData.toolClassName + " object", e);
648             }
649         }
650     }
651 
652     /**
653      * Populate the given context with the session-scoped tools.
654      *
655      * @param tools The list of tools with which to populate the
656      * session.
657      * @param context The context to populate.
658      * @param pipelineData The current RunData object
659      * @param user The <code>User</code> object whose storage to
660      * retrieve the tool from.
661      */
662     private void populateWithSessionTools(List<ToolData> tools, Context context,
663             RunData data, User user)
664     {
665         // Iterate the tools
666         for (Iterator<ToolData> it = tools.iterator(); it.hasNext();)
667         {
668             ToolData toolData = it.next();
669             try
670             {
671                 // ensure that tool is created only once for a user
672                 // by synchronizing against the user object
673                 synchronized (data.getSession())
674                 {
675                     // first try and fetch the tool from the user's
676                     // hashtable
677                     Object tool = data.getSession().getAttribute(
678                             SESSION_TOOLS_ATTRIBUTE_PREFIX
679                             + toolData.toolClassName);
680 
681                     if (tool == null)
682                     {
683                         // if not there, an instance must be fetched from
684                         // the pool
685                         tool = pool.getInstance(toolData.toolClass);
686 
687                         // session tools are init'd with the User object
688                         initTool(tool, user);
689                     }
690 
691                     // *NOT* else
692                     if(tool != null)
693                     {
694                         // store the newly created tool in the session
695                         data.getSession().setAttribute(
696                                 SESSION_TOOLS_ATTRIBUTE_PREFIX
697                                 + tool.getClass().getName(), tool);
698 
699                         // This is a semantics change. In the old
700                         // Turbine, Session tools were initialized and
701                         // then refreshed every time they were pulled
702                         // into the context if "refreshToolsPerRequest"
703                         // was wanted.
704                         //
705                         // RunDataApplicationTools now have a parameter
706                         // for refresh. If it is not refreshed immediately
707                         // after init(), the parameter value will be undefined
708                         // until the 2nd run. So we refresh all the session
709                         // tools on every run, even if we just init'ed it.
710                         //
711 
712                         if (refreshToolsPerRequest)
713                         {
714                             refreshTool(tool, data);
715                         }
716 
717                         // put the tool in the context
718                         log.debug("Adding " + tool + " to ctx as "
719                                 + toolData.toolName);
720                         context.put(toolData.toolName, tool);
721                     }
722                     else
723                     {
724                         log.info("Tool " + toolData.toolName
725                                 + " was null, skipping it.");
726                     }
727                 }
728             }
729             catch (Exception e)
730             {
731                 log.error("Could not instantiate session tool "
732                     + toolData.toolName + " from a "
733                     + toolData.toolClassName + " object", e);
734             }
735         }
736     }
737 
738 
739 
740     /**
741      * Populate the given context with the perm-scoped tools.
742      *
743      * @param tools The list of tools with which to populate the
744      * session.
745      * @param context The context to populate.
746      * @param pipelineData The current RunData object
747      * @param user The <code>User</code> object whose storage to
748      * retrieve the tool from.
749      */
750     private void populateWithPermTools(List<ToolData> tools, Context context,
751             PipelineData pipelineData, User user)
752     {
753         // Iterate the tools
754         for (Iterator<ToolData> it = tools.iterator(); it.hasNext();)
755         {
756             ToolData toolData = it.next();
757             try
758             {
759                 // ensure that tool is created only once for a user
760                 // by synchronizing against the user object
761                 synchronized (user)
762                 {
763                     // first try and fetch the tool from the user's
764                     // hashtable
765                     Object tool = user.getPerm(toolData.toolClassName);
766 
767                     if (tool == null)
768                     {
769                         // if not there, an instance must be fetched from
770                         // the pool
771                         tool = pool.getInstance(toolData.toolClass);
772 
773                         // session tools are init'd with the User object
774                         initTool(tool, user);
775 
776                         // store the newly created tool in the user's hashtable
777                         user.setPerm(toolData.toolClassName, tool);
778                     }
779 
780                     // *NOT* else
781                     if(tool != null)
782                     {
783                         // This is a semantics change. In the old
784                         // Turbine, Session tools were initialized and
785                         // then refreshed every time they were pulled
786                         // into the context if "refreshToolsPerRequest"
787                         // was wanted.
788                         //
789                         // RunDataApplicationTools now have a parameter
790                         // for refresh. If it is not refreshed immediately
791                         // after init(), the parameter value will be undefined
792                         // until the 2nd run. So we refresh all the session
793                         // tools on every run, even if we just init'ed it.
794                         //
795 
796                         if (refreshToolsPerRequest)
797                         {
798                             refreshTool(tool, pipelineData);
799                         }
800 
801                         // put the tool in the context
802                         log.debug("Adding " + tool + " to ctx as "
803                                 + toolData.toolName);
804                         log.warn("Persistent scope tools are deprecated.");
805                         context.put(toolData.toolName, tool);
806                     }
807                     else
808                     {
809                         log.info("Tool " + toolData.toolName
810                                 + " was null, skipping it.");
811                     }
812                 }
813             }
814             catch (Exception e)
815             {
816                 log.error("Could not instantiate perm tool "
817                     + toolData.toolName + " from a "
818                     + toolData.toolClassName + " object", e);
819             }
820         }
821     }
822 
823     /**
824      * Populate the given context with the perm-scoped tools.
825      *
826      * @param tools The list of tools with which to populate the
827      * session.
828      * @param context The context to populate.
829      * @param pipelineData The current RunData object
830      * @param user The <code>User</code> object whose storage to
831      * retrieve the tool from.
832      */
833     private void populateWithPermTools(List<ToolData> tools, Context context,
834             RunData data, User user)
835     {
836         // Iterate the tools
837         for (Iterator<ToolData> it = tools.iterator(); it.hasNext();)
838         {
839             ToolData toolData = it.next();
840             try
841             {
842                 // ensure that tool is created only once for a user
843                 // by synchronizing against the user object
844                 synchronized (user)
845                 {
846                     // first try and fetch the tool from the user's
847                     // hashtable
848                     Object tool = user.getPerm(toolData.toolClassName);
849 
850                     if (tool == null)
851                     {
852                         // if not there, an instance must be fetched from
853                         // the pool
854                         tool = pool.getInstance(toolData.toolClass);
855 
856                         // session tools are init'd with the User object
857                         initTool(tool, user);
858 
859                         // store the newly created tool in the user's hashtable
860                         user.setPerm(toolData.toolClassName, tool);
861                     }
862 
863                     // *NOT* else
864                     if(tool != null)
865                     {
866                         // This is a semantics change. In the old
867                         // Turbine, Session tools were initialized and
868                         // then refreshed every time they were pulled
869                         // into the context if "refreshToolsPerRequest"
870                         // was wanted.
871                         //
872                         // RunDataApplicationTools now have a parameter
873                         // for refresh. If it is not refreshed immediately
874                         // after init(), the parameter value will be undefined
875                         // until the 2nd run. So we refresh all the session
876                         // tools on every run, even if we just init'ed it.
877                         //
878 
879                         if (refreshToolsPerRequest)
880                         {
881                             refreshTool(tool, data);
882                         }
883 
884                         // put the tool in the context
885                         log.debug("Adding " + tool + " to ctx as "
886                                 + toolData.toolName);
887                         log.warn("Persistent scope tools are deprecated.");
888                         context.put(toolData.toolName, tool);
889                     }
890                     else
891                     {
892                         log.info("Tool " + toolData.toolName
893                                 + " was null, skipping it.");
894                     }
895                 }
896             }
897             catch (Exception e)
898             {
899                 log.error("Could not instantiate perm tool "
900                     + toolData.toolName + " from a "
901                     + toolData.toolClassName + " object", e);
902             }
903         }
904     }
905 
906 
907 
908     /**
909      * Return the absolute path to the resources directory
910      * used by the application tools.
911      *
912      * @return the absolute path of the resources directory
913      */
914     @Override
915     public String getAbsolutePathToResourcesDirectory()
916     {
917         return Turbine.getRealPath(resourcesDirectory);
918     }
919 
920     /**
921      * Return the resources directory. This is
922      * relative to the web context.
923      *
924      * @return the relative path of the resources directory
925      */
926     @Override
927     public String getResourcesDirectory()
928     {
929         return resourcesDirectory;
930     }
931 
932     /**
933      * Refresh the global tools. We can
934      * only refresh those tools that adhere to
935      * ApplicationTool interface because we
936      * know those types of tools have a refresh
937      * method.
938      */
939     private void refreshGlobalTools()
940     {
941         for (Iterator<ToolData> it = globalTools.iterator(); it.hasNext();)
942         {
943             ToolData toolData = it.next();
944             Object tool = globalContext.get(toolData.toolName);
945             refreshTool(tool, null);
946         }
947     }
948 
949     /**
950      * Release the request-scope tool instances in the
951      * given Context back to the pool
952      *
953      * @param context the Velocity Context to release tools from
954      */
955     @Override
956     public void releaseTools(Context context)
957     {
958         // only the request tools can be released - other scoped
959         // tools will have continuing references to them
960         releaseTools(context, requestTools);
961     }
962 
963     /**
964      * Release the given list of tools from the context back
965      * to the pool
966      *
967      * @param context the Context containing the tools
968      * @param tools a List of ToolData objects
969      */
970     private void releaseTools(Context context, List<ToolData> tools)
971     {
972         for (Iterator<ToolData> it = tools.iterator(); it.hasNext();)
973         {
974             ToolData toolData = it.next();
975             Object tool = context.remove(toolData.toolName);
976 
977             if (tool != null)
978             {
979                 pool.putInstance(tool);
980             }
981         }
982     }
983 
984     /**
985      * Initialized a given Tool with the passed init Object
986      *
987      * @param tool A Tool Object
988      * @param param The Init Parameter
989      *
990      * @throws Exception If anything went wrong.
991      */
992     private void initTool(Object tool, Object param)
993         throws Exception
994     {
995         if (param instanceof PipelineData)
996         {
997             if (tool instanceof PipelineDataApplicationTool)
998             {
999                 ((PipelineDataApplicationTool) tool).init(param);
1000             }
1001             else if (tool instanceof RunDataApplicationTool)
1002             {
1003                 RunData data = getRunData((PipelineData)param);
1004                 ((RunDataApplicationTool) tool).init(data);
1005             }
1006             else if (tool instanceof ApplicationTool)
1007             {
1008                 RunData data = getRunData((PipelineData)param);
1009                 ((ApplicationTool) tool).init(data);
1010             }
1011         }
1012         else
1013         {
1014             if (tool instanceof PipelineDataApplicationTool)
1015             {
1016                 ((PipelineDataApplicationTool) tool).init(param);
1017             }
1018             else if (tool instanceof RunDataApplicationTool)
1019             {
1020                 ((RunDataApplicationTool) tool).init(param);
1021             }
1022             else if (tool instanceof ApplicationTool)
1023             {
1024                 ((ApplicationTool) tool).init(param);
1025             }
1026         }
1027     }
1028 
1029     /**
1030      * Refresh a given Tool.
1031      *
1032      * @param tool A Tool Object
1033      * @param pipelineData The current RunData Object
1034      */
1035     private void refreshTool(Object tool, Object dataObject)
1036     {
1037         RunData data = null;
1038         PipelineData pipelineData = null;
1039         if (dataObject instanceof PipelineData)
1040         {
1041             pipelineData = (PipelineData)dataObject;
1042             data = getRunData(pipelineData);
1043             if (tool instanceof PipelineDataApplicationTool)
1044             {
1045                 ((PipelineDataApplicationTool) tool).refresh(pipelineData);
1046             }
1047         }
1048         if (tool instanceof ApplicationTool)
1049         {
1050             ((ApplicationTool) tool).refresh();
1051         }
1052         else if (tool instanceof RunDataApplicationTool)
1053         {
1054             ((RunDataApplicationTool) tool).refresh(data);
1055         }
1056     }
1057 
1058     private RunData getRunData(PipelineData pipelineData)
1059     {
1060         if(!(pipelineData instanceof RunData)){
1061             throw new RuntimeException("Can't cast to rundata from pipeline data.");
1062         }
1063         return (RunData)pipelineData;
1064     }
1065 }