001package org.apache.turbine.services.pull;
002
003
004/*
005 * Licensed to the Apache Software Foundation (ASF) under one
006 * or more contributor license agreements.  See the NOTICE file
007 * distributed with this work for additional information
008 * regarding copyright ownership.  The ASF licenses this file
009 * to you under the Apache License, Version 2.0 (the
010 * "License"); you may not use this file except in compliance
011 * with the License.  You may obtain a copy of the License at
012 *
013 *   http://www.apache.org/licenses/LICENSE-2.0
014 *
015 * Unless required by applicable law or agreed to in writing,
016 * software distributed under the License is distributed on an
017 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
018 * KIND, either express or implied.  See the License for the
019 * specific language governing permissions and limitations
020 * under the License.
021 */
022
023
024import java.util.ArrayList;
025import java.util.Iterator;
026import java.util.List;
027
028import org.apache.commons.configuration.Configuration;
029import org.apache.commons.logging.Log;
030import org.apache.commons.logging.LogFactory;
031import org.apache.fulcrum.pool.PoolService;
032import org.apache.fulcrum.security.model.turbine.TurbineUserManager;
033import org.apache.turbine.Turbine;
034import org.apache.turbine.om.security.User;
035import org.apache.turbine.pipeline.PipelineData;
036import org.apache.turbine.services.InitializationException;
037import org.apache.turbine.services.TurbineBaseService;
038import org.apache.turbine.services.TurbineServices;
039import org.apache.turbine.services.velocity.TurbineVelocity;
040import org.apache.turbine.services.velocity.VelocityService;
041import org.apache.turbine.util.RunData;
042import org.apache.velocity.context.Context;
043
044/**
045 * This is the concrete implementation of the Turbine
046 * Pull Service.
047 * <p>
048 * These are tools that are placed in the context by the service
049 * These tools will be made available to all your
050 * templates. You list the tools in the following way:
051 * <p>
052 * <pre>
053 * tool.&lt;scope&gt;.&lt;id&gt; = &lt;classname&gt;
054 *
055 * &lt;scope&gt;      is the tool scope: global, request, session,
056 *              authorized or persistent (see below for more details)
057 * &lt;id&gt;         is the name of the tool in the context
058 *
059 * You can configure the tools in this way:
060 * tool.&lt;id&gt;.&lt;parameter&gt; = &lt;value&gt;
061 *
062 * So if you find "global", "request", "session" or "persistent" as second
063 * part, it is a configuration to put a tool into the toolbox, else it is a
064 * tool specific configuration.
065 *
066 * For example:
067 *
068 * tool.global.ui    = org.apache.turbine.util.pull.UIManager
069 * tool.global.mm    = org.apache.turbine.util.pull.MessageManager
070 * tool.request.link = org.apache.turbine.services.pull.tools.TemplateLink
071 * tool.request.page = org.apache.turbine.util.template.TemplatePageAttributes
072 *
073 * Then:
074 *
075 * tool.ui.skin = default
076 *
077 * configures the value of "skin" for the "ui" tool.
078 *
079 * Tools are accessible in all templates by the <id> given
080 * to the tool. So for the above listings the UIManager would
081 * be available as $ui, the MessageManager as $mm, the TemplateLink
082 * as $link and the TemplatePageAttributes as $page.
083 *
084 * You should avoid using tool names called "global", "request",
085 * "session" or "persistent" because of clashes with the possible Scopes.
086 *
087 * Scopes:
088 *
089 *  global:     tool is instantiated once and that instance is available
090 *              to all templates for all requests. Tool must be threadsafe.
091 *
092 *  request:    tool is instantiated once for each request (although the
093 *              PoolService is used to recycle instances). Tool need not
094 *              be threadsafe.
095 *
096 *  session:    tool is instantiated once for each user session, and is
097 *              stored in the session.  These tools do not need to be
098 *              threadsafe.
099 *
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 */
121public 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}