001    package 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    
024    import java.util.ArrayList;
025    import java.util.Iterator;
026    import java.util.List;
027    
028    import org.apache.commons.configuration.Configuration;
029    import org.apache.commons.logging.Log;
030    import org.apache.commons.logging.LogFactory;
031    import org.apache.fulcrum.pool.PoolService;
032    import org.apache.turbine.Turbine;
033    import org.apache.turbine.om.security.User;
034    import org.apache.turbine.pipeline.PipelineData;
035    import org.apache.turbine.services.InitializationException;
036    import org.apache.turbine.services.TurbineBaseService;
037    import org.apache.turbine.services.TurbineServices;
038    import org.apache.turbine.services.security.TurbineSecurity;
039    import org.apache.turbine.services.velocity.TurbineVelocity;
040    import org.apache.turbine.services.velocity.VelocityService;
041    import org.apache.turbine.util.RunData;
042    import 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 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    }