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.turbine.Turbine;
33  import org.apache.turbine.om.security.User;
34  import org.apache.turbine.pipeline.PipelineData;
35  import org.apache.turbine.services.InitializationException;
36  import org.apache.turbine.services.TurbineBaseService;
37  import org.apache.turbine.services.TurbineServices;
38  import org.apache.turbine.services.security.TurbineSecurity;
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 1078552 2011-03-06 19:58:46Z 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     public Context getGlobalContext()
362     {
363         if (refreshToolsPerRequest)
364         {
365             refreshGlobalTools();
366         }
367         return globalContext;
368     }
369 
370     /**
371      * Populate the given context with all request, session, authorized
372      * and persistent scope tools (it is assumed that the context
373      * already wraps the global context, and thus already contains
374      * the global tools).
375      *
376      * @param context a Velocity Context to populate
377      * @param data a RunData object for request specific data
378      */
379     public void populateContext(Context context, RunData data)
380     {
381         populateWithRequestTools(context, data);
382 
383         // session tools (whether session-only or persistent are
384         // very similar, so the same method is used - the
385         // boolean parameter indicates whether get/setPerm is to be used
386         // rather than get/setTemp)
387 
388         //
389         // Session Tool start right at the session once the user has been set
390         // while persistent and authorized Tools are started when the user has
391         // logged in
392         //
393         User user = data.getUser();
394 
395         // Note: Session tools are currently lost after the login action
396         // because the anonymous user is replaced the the real user object.
397         // We should either store the session pull tools in the session or
398         // make Turbine.loginAction() copy the session pull tools into the
399         // new user object.
400         populateWithSessionTools(sessionTools, context, data, user);
401 
402         if (!TurbineSecurity.isAnonymousUser(user))
403         {
404             if (user.hasLoggedIn())
405             {
406                 populateWithSessionTools(authorizedTools, context, data, user);
407                 populateWithPermTools(persistentTools, context, data, user);
408             }
409         }
410     }
411 
412     /**
413      * Populate the given context with all request, session, authorized
414      * and persistent scope tools (it is assumed that the context
415      * already wraps the global context, and thus already contains
416      * the global tools).
417      *
418      * @param context a Velocity Context to populate
419      * @param data a PipelineData object for request specific data
420      */
421     public void populateContext(Context context, PipelineData pipelineData)
422     {
423        // Map runDataMap = (Map) pipelineData.get(RunData.class);
424        // RunData data = (RunData)runDataMap.get(RunData.class);
425         RunData data = (RunData)pipelineData;
426 
427         populateWithRequestTools(context, pipelineData);
428         // session tools (whether session-only or persistent are
429         // very similar, so the same method is used - the
430         // boolean parameter indicates whether get/setPerm is to be used
431         // rather than get/setTemp)
432 
433         //
434         // Session Tool start right at the session once the user has been set
435         // while persistent and authorized Tools are started when the user has
436         // logged in
437         //
438         User user = data.getUser();
439 
440         // Note: Session tools are currently lost after the login action
441         // because the anonymous user is replaced the the real user object.
442         // We should either store the session pull tools in the session or
443         // make Turbine.loginAction() copy the session pull tools into the
444         // new user object.
445         populateWithSessionTools(sessionTools, context, pipelineData, user);
446 
447         if (!TurbineSecurity.isAnonymousUser(user))
448         {
449             if (user.hasLoggedIn())
450             {
451                 populateWithSessionTools(authorizedTools, context, pipelineData, user);
452                 populateWithPermTools(persistentTools, context, pipelineData, user);
453             }
454         }
455     }
456 
457     /**
458      * Populate the given context with the global tools
459      *
460      * @param context a Velocity Context to populate
461      */
462     private void populateWithGlobalTools(Context context)
463     {
464         for (Iterator<ToolData> it = globalTools.iterator(); it.hasNext();)
465         {
466             ToolData toolData = it.next();
467             try
468             {
469                 Object tool = toolData.toolClass.newInstance();
470 
471                 // global tools are init'd with a null data parameter
472                 initTool(tool, null);
473 
474                 // put the tool in the context
475                 context.put(toolData.toolName, tool);
476             }
477             catch (Exception e)
478             {
479                 log.error("Could not instantiate global tool "
480                     + toolData.toolName + " from a "
481                     + toolData.toolClassName + " object", e);
482             }
483         }
484     }
485 
486     /**
487      * Populate the given context with the request-scope tools
488      *
489      * @param context a Velocity Context to populate
490      * @param data a RunData instance
491      */
492     private void populateWithRequestTools(Context context, RunData data)
493     {
494         // Iterate the tools
495         for (Iterator<ToolData> it = requestTools.iterator(); it.hasNext();)
496         {
497             ToolData toolData = it.next();
498             try
499             {
500                 // Fetch Object through the Pool.
501                 Object tool = pool.getInstance(toolData.toolClass);
502 
503                 // request tools are init'd with a RunData object
504                 initTool(tool, data);
505 
506                 // put the tool in the context
507                 context.put(toolData.toolName, tool);
508             }
509             catch (Exception e)
510             {
511                 log.error("Could not instantiate request tool "
512                     + toolData.toolName + " from a "
513                     + toolData.toolClassName + " object", e);
514             }
515         }
516     }
517 
518 
519     /**
520      * Populate the given context with the request-scope tools
521      *
522      * @param context a Velocity Context to populate
523      * @param data a RunData instance
524      */
525     private void populateWithRequestTools(Context context, PipelineData pipelineData)
526     {
527         // Iterate the tools
528         for (Iterator<ToolData> it = requestTools.iterator(); it.hasNext();)
529         {
530             ToolData toolData = it.next();
531             try
532             {
533                 // Fetch Object through the Pool.
534                 Object tool = pool.getInstance(toolData.toolClass);
535 
536                 initTool(tool, pipelineData);
537 
538                 // put the tool in the context
539                 context.put(toolData.toolName, tool);
540             }
541             catch (Exception e)
542             {
543                 log.error("Could not instantiate request tool "
544                     + toolData.toolName + " from a "
545                     + toolData.toolClassName + " object", e);
546             }
547         }
548     }
549 
550     /**
551      * Populate the given context with the session-scoped tools.
552      *
553      * @param tools The list of tools with which to populate the
554      * session.
555      * @param context The context to populate.
556      * @param data The current RunData object
557      * @param user The <code>User</code> object whose storage to
558      * retrieve the tool from.
559      */
560     @SuppressWarnings({ "unused", "null" })
561     private void populateWithSessionTools(List<ToolData> tools, Context context,
562             PipelineData pipelineData, User user)
563     {
564         //Map runDataMap = (Map)pipelineData.get(RunData.class);
565         //RunData data = (RunData) runDataMap.get(RunData.class);
566         RunData runData = (RunData)pipelineData;
567         // Iterate the tools
568         for (Iterator<ToolData> it = tools.iterator(); it.hasNext();)
569         {
570             ToolData toolData = it.next();
571             try
572             {
573                 // ensure that tool is created only once for a user
574                 // by synchronizing against the user object
575                 synchronized (runData.getSession())
576                 {
577                     // first try and fetch the tool from the user's
578                     // hashtable
579                     Object tool = runData.getSession().getAttribute(
580                             SESSION_TOOLS_ATTRIBUTE_PREFIX
581                             + toolData.toolClassName);
582 
583                     if (tool == null)
584                     {
585                         // if not there, an instance must be fetched from
586                         // the pool
587                         tool = pool.getInstance(toolData.toolClass);
588 
589                         // session tools are init'd with the User object
590                         initTool(tool, user);
591 
592                         // store the newly created tool in the session
593                         runData.getSession().setAttribute(
594                                 SESSION_TOOLS_ATTRIBUTE_PREFIX
595                                 + tool.getClass().getName(), tool);
596                     }
597 
598                     // *NOT* else
599                     if(tool != null)
600                     {
601                         // This is a semantics change. In the old
602                         // Turbine, Session tools were initialized and
603                         // then refreshed every time they were pulled
604                         // into the context if "refreshToolsPerRequest"
605                         // was wanted.
606                         //
607                         // RunDataApplicationTools now have a parameter
608                         // for refresh. If it is not refreshed immediately
609                         // after init(), the parameter value will be undefined
610                         // until the 2nd run. So we refresh all the session
611                         // tools on every run, even if we just init'ed it.
612                         //
613 
614                         if (refreshToolsPerRequest)
615                         {
616                             refreshTool(tool, pipelineData);
617                         }
618 
619                         // put the tool in the context
620                         log.debug("Adding " + tool + " to ctx as "
621                                 + toolData.toolName);
622                         context.put(toolData.toolName, tool);
623                     }
624                     else
625                     {
626                         log.info("Tool " + toolData.toolName
627                                 + " was null, skipping it.");
628                     }
629                 }
630             }
631             catch (Exception e)
632             {
633                 log.error("Could not instantiate session tool "
634                     + toolData.toolName + " from a "
635                     + toolData.toolClassName + " object", e);
636             }
637         }
638     }
639 
640     /**
641      * Populate the given context with the session-scoped tools.
642      *
643      * @param tools The list of tools with which to populate the
644      * session.
645      * @param context The context to populate.
646      * @param data The current RunData object
647      * @param user The <code>User</code> object whose storage to
648      * retrieve the tool from.
649      */
650     @SuppressWarnings({ "unused", "null" })
651     private void populateWithSessionTools(List<ToolData> tools, Context context,
652             RunData data, User user)
653     {
654         // Iterate the tools
655         for (Iterator<ToolData> it = tools.iterator(); it.hasNext();)
656         {
657             ToolData toolData = it.next();
658             try
659             {
660                 // ensure that tool is created only once for a user
661                 // by synchronizing against the user object
662                 synchronized (data.getSession())
663                 {
664                     // first try and fetch the tool from the user's
665                     // hashtable
666                     Object tool = data.getSession().getAttribute(
667                             SESSION_TOOLS_ATTRIBUTE_PREFIX
668                             + toolData.toolClassName);
669 
670                     if (tool == null)
671                     {
672                         // if not there, an instance must be fetched from
673                         // the pool
674                         tool = pool.getInstance(toolData.toolClass);
675 
676                         // session tools are init'd with the User object
677                         initTool(tool, user);
678 
679                         // store the newly created tool in the session
680                         data.getSession().setAttribute(
681                                 SESSION_TOOLS_ATTRIBUTE_PREFIX
682                                 + tool.getClass().getName(), tool);
683                     }
684 
685                     // *NOT* else
686                     if(tool != null)
687                     {
688                         // This is a semantics change. In the old
689                         // Turbine, Session tools were initialized and
690                         // then refreshed every time they were pulled
691                         // into the context if "refreshToolsPerRequest"
692                         // was wanted.
693                         //
694                         // RunDataApplicationTools now have a parameter
695                         // for refresh. If it is not refreshed immediately
696                         // after init(), the parameter value will be undefined
697                         // until the 2nd run. So we refresh all the session
698                         // tools on every run, even if we just init'ed it.
699                         //
700 
701                         if (refreshToolsPerRequest)
702                         {
703                             refreshTool(tool, data);
704                         }
705 
706                         // put the tool in the context
707                         log.debug("Adding " + tool + " to ctx as "
708                                 + toolData.toolName);
709                         context.put(toolData.toolName, tool);
710                     }
711                     else
712                     {
713                         log.info("Tool " + toolData.toolName
714                                 + " was null, skipping it.");
715                     }
716                 }
717             }
718             catch (Exception e)
719             {
720                 log.error("Could not instantiate session tool "
721                     + toolData.toolName + " from a "
722                     + toolData.toolClassName + " object", e);
723             }
724         }
725     }
726 
727 
728 
729     /**
730      * Populate the given context with the perm-scoped tools.
731      *
732      * @param tools The list of tools with which to populate the
733      * session.
734      * @param context The context to populate.
735      * @param data The current RunData object
736      * @param user The <code>User</code> object whose storage to
737      * retrieve the tool from.
738      */
739     private void populateWithPermTools(List<ToolData> tools, Context context,
740             PipelineData pipelineData, User user)
741     {
742         // Iterate the tools
743         for (Iterator<ToolData> it = tools.iterator(); it.hasNext();)
744         {
745             ToolData toolData = it.next();
746             try
747             {
748                 // ensure that tool is created only once for a user
749                 // by synchronizing against the user object
750                 synchronized (user)
751                 {
752                     // first try and fetch the tool from the user's
753                     // hashtable
754                     Object tool = user.getPerm(toolData.toolClassName);
755 
756                     if (tool == null)
757                     {
758                         // if not there, an instance must be fetched from
759                         // the pool
760                         tool = pool.getInstance(toolData.toolClass);
761 
762                         // session tools are init'd with the User object
763                         initTool(tool, user);
764 
765                         // store the newly created tool in the user's hashtable
766                         user.setPerm(toolData.toolClassName, tool);
767                     }
768 
769                     // *NOT* else
770                     if(tool != null)
771                     {
772                         // This is a semantics change. In the old
773                         // Turbine, Session tools were initialized and
774                         // then refreshed every time they were pulled
775                         // into the context if "refreshToolsPerRequest"
776                         // was wanted.
777                         //
778                         // RunDataApplicationTools now have a parameter
779                         // for refresh. If it is not refreshed immediately
780                         // after init(), the parameter value will be undefined
781                         // until the 2nd run. So we refresh all the session
782                         // tools on every run, even if we just init'ed it.
783                         //
784 
785                         if (refreshToolsPerRequest)
786                         {
787                             refreshTool(tool, pipelineData);
788                         }
789 
790                         // put the tool in the context
791                         log.debug("Adding " + tool + " to ctx as "
792                                 + toolData.toolName);
793                         log.warn("Persistent scope tools are deprecated.");
794                         context.put(toolData.toolName, tool);
795                     }
796                     else
797                     {
798                         log.info("Tool " + toolData.toolName
799                                 + " was null, skipping it.");
800                     }
801                 }
802             }
803             catch (Exception e)
804             {
805                 log.error("Could not instantiate perm tool "
806                     + toolData.toolName + " from a "
807                     + toolData.toolClassName + " object", e);
808             }
809         }
810     }
811 
812     /**
813      * Populate the given context with the perm-scoped tools.
814      *
815      * @param tools The list of tools with which to populate the
816      * session.
817      * @param context The context to populate.
818      * @param data The current RunData object
819      * @param user The <code>User</code> object whose storage to
820      * retrieve the tool from.
821      */
822     private void populateWithPermTools(List<ToolData> tools, Context context,
823             RunData data, User user)
824     {
825         // Iterate the tools
826         for (Iterator<ToolData> it = tools.iterator(); it.hasNext();)
827         {
828             ToolData toolData = it.next();
829             try
830             {
831                 // ensure that tool is created only once for a user
832                 // by synchronizing against the user object
833                 synchronized (user)
834                 {
835                     // first try and fetch the tool from the user's
836                     // hashtable
837                     Object tool = user.getPerm(toolData.toolClassName);
838 
839                     if (tool == null)
840                     {
841                         // if not there, an instance must be fetched from
842                         // the pool
843                         tool = pool.getInstance(toolData.toolClass);
844 
845                         // session tools are init'd with the User object
846                         initTool(tool, user);
847 
848                         // store the newly created tool in the user's hashtable
849                         user.setPerm(toolData.toolClassName, tool);
850                     }
851 
852                     // *NOT* else
853                     if(tool != null)
854                     {
855                         // This is a semantics change. In the old
856                         // Turbine, Session tools were initialized and
857                         // then refreshed every time they were pulled
858                         // into the context if "refreshToolsPerRequest"
859                         // was wanted.
860                         //
861                         // RunDataApplicationTools now have a parameter
862                         // for refresh. If it is not refreshed immediately
863                         // after init(), the parameter value will be undefined
864                         // until the 2nd run. So we refresh all the session
865                         // tools on every run, even if we just init'ed it.
866                         //
867 
868                         if (refreshToolsPerRequest)
869                         {
870                             refreshTool(tool, data);
871                         }
872 
873                         // put the tool in the context
874                         log.debug("Adding " + tool + " to ctx as "
875                                 + toolData.toolName);
876                         log.warn("Persistent scope tools are deprecated.");
877                         context.put(toolData.toolName, tool);
878                     }
879                     else
880                     {
881                         log.info("Tool " + toolData.toolName
882                                 + " was null, skipping it.");
883                     }
884                 }
885             }
886             catch (Exception e)
887             {
888                 log.error("Could not instantiate perm tool "
889                     + toolData.toolName + " from a "
890                     + toolData.toolClassName + " object", e);
891             }
892         }
893     }
894 
895 
896 
897     /**
898      * Return the absolute path to the resources directory
899      * used by the application tools.
900      *
901      * @return the absolute path of the resources directory
902      */
903     public String getAbsolutePathToResourcesDirectory()
904     {
905         return Turbine.getRealPath(resourcesDirectory);
906     }
907 
908     /**
909      * Return the resources directory. This is
910      * relative to the web context.
911      *
912      * @return the relative path of the resources directory
913      */
914     public String getResourcesDirectory()
915     {
916         return resourcesDirectory;
917     }
918 
919     /**
920      * Refresh the global tools. We can
921      * only refresh those tools that adhere to
922      * ApplicationTool interface because we
923      * know those types of tools have a refresh
924      * method.
925      */
926     private void refreshGlobalTools()
927     {
928         for (Iterator<ToolData> it = globalTools.iterator(); it.hasNext();)
929         {
930             ToolData toolData = it.next();
931             Object tool = globalContext.get(toolData.toolName);
932             refreshTool(tool, null);
933         }
934     }
935 
936     /**
937      * Release the request-scope tool instances in the
938      * given Context back to the pool
939      *
940      * @param context the Velocity Context to release tools from
941      */
942     public void releaseTools(Context context)
943     {
944         // only the request tools can be released - other scoped
945         // tools will have continuing references to them
946         releaseTools(context, requestTools);
947     }
948 
949     /**
950      * Release the given list of tools from the context back
951      * to the pool
952      *
953      * @param context the Context containing the tools
954      * @param tools a List of ToolData objects
955      */
956     private void releaseTools(Context context, List<ToolData> tools)
957     {
958         for (Iterator<ToolData> it = tools.iterator(); it.hasNext();)
959         {
960             ToolData toolData = it.next();
961             Object tool = context.remove(toolData.toolName);
962 
963             if (tool != null)
964             {
965                 pool.putInstance(tool);
966             }
967         }
968     }
969 
970     /**
971      * Initialized a given Tool with the passed init Object
972      *
973      * @param tool A Tool Object
974      * @param param The Init Parameter
975      *
976      * @throws Exception If anything went wrong.
977      */
978     private void initTool(Object tool, Object param)
979         throws Exception
980     {
981         if (param instanceof PipelineData)
982         {
983             if (tool instanceof PipelineDataApplicationTool)
984             {
985                 ((PipelineDataApplicationTool) tool).init(param);
986             }
987             else if (tool instanceof RunDataApplicationTool)
988             {
989                 RunData data = getRunData((PipelineData)param);
990                 ((RunDataApplicationTool) tool).init(data);
991             }
992             else if (tool instanceof ApplicationTool)
993             {
994                 RunData data = getRunData((PipelineData)param);
995                 ((ApplicationTool) tool).init(data);
996             }
997         }
998         else
999         {
1000             if (tool instanceof PipelineDataApplicationTool)
1001             {
1002                 ((PipelineDataApplicationTool) tool).init(param);
1003             }
1004             else if (tool instanceof RunDataApplicationTool)
1005             {
1006                 ((RunDataApplicationTool) tool).init(param);
1007             }
1008             else if (tool instanceof ApplicationTool)
1009             {
1010                 ((ApplicationTool) tool).init(param);
1011             }
1012         }
1013     }
1014 
1015     /**
1016      * Refresh a given Tool.
1017      *
1018      * @param tool A Tool Object
1019      * @param data The current RunData Object
1020      */
1021     private void refreshTool(Object tool, Object dataObject)
1022     {
1023         RunData data = null;
1024         PipelineData pipelineData = null;
1025         if (dataObject instanceof PipelineData)
1026         {
1027             pipelineData = (PipelineData)dataObject;
1028             data = getRunData(pipelineData);
1029             if (tool instanceof PipelineDataApplicationTool)
1030             {
1031                 ((PipelineDataApplicationTool) tool).refresh(pipelineData);
1032             }
1033         }
1034         if (tool instanceof ApplicationTool)
1035         {
1036             ((ApplicationTool) tool).refresh();
1037         }
1038         else if (tool instanceof RunDataApplicationTool)
1039         {
1040             ((RunDataApplicationTool) tool).refresh(data);
1041         }
1042     }
1043 
1044     private RunData getRunData(PipelineData pipelineData)
1045     {
1046         if(!(pipelineData instanceof RunData)){
1047             throw new RuntimeException("Can't cast to rundata from pipeline data.");
1048         }
1049         return (RunData)pipelineData;
1050     }
1051 }