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.annotation.AnnotationProcessor;
035import org.apache.turbine.om.security.User;
036import org.apache.turbine.pipeline.PipelineData;
037import org.apache.turbine.services.InitializationException;
038import org.apache.turbine.services.TurbineBaseService;
039import org.apache.turbine.services.TurbineServices;
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 &lt;id&gt; 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 1773378 2016-12-09 13:19:59Z 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 = (VelocityService)TurbineServices.getInstance().getService(VelocityService.SERVICE_NAME);
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("TurbinePullService failed to initialize", e);
222        }
223    }
224
225    /**
226     * Initialize the pull service
227     *
228     * @throws Exception A problem happened when starting up
229     */
230    private void initPullService()
231        throws Exception
232    {
233        // This is the per-service configuration, prefixed with services.PullService
234        Configuration conf = getConfiguration();
235
236        // Get the resources directory that is specificed
237        // in the TR.props or default to "resources", relative to the webapp.
238        resourcesDirectory = conf.getString(
239            TOOL_RESOURCES_DIR_KEY,
240            TOOL_RESOURCES_DIR_DEFAULT);
241
242        // Should we refresh the tool box on a per
243        // request basis.
244        refreshToolsPerRequest =
245            conf.getBoolean(
246                TOOLS_PER_REQUEST_REFRESH_KEY,
247                TOOLS_PER_REQUEST_REFRESH_DEFAULT);
248
249        // Log the fact that the application tool box will
250        // be refreshed on a per request basis.
251        if (refreshToolsPerRequest)
252        {
253            log.info("Pull Model tools will "
254                + "be refreshed on a per request basis.");
255        }
256    }
257
258    /**
259     * Initialize the pull tools. At this point, the
260     * service must be marked as initialized, because the
261     * tools may call the methods of this service via the
262     * static facade class TurbinePull.
263     *
264     * @throws Exception A problem happened when starting up
265     */
266    private void initPullTools()
267        throws Exception
268    {
269        // And for reasons I never really fully understood,
270        // the tools directive is toplevel without the service
271        // prefix. This is brain-damaged but for legacy reasons we
272        // keep this. So this is the global turbine configuration:
273        Configuration conf = Turbine.getConfiguration();
274
275        // Grab each list of tools that are to be used (for global scope,
276        // request scope, authorized scope, session scope and persistent
277        // scope tools). They are specified respectively in the TR.props
278        // like this:
279        //
280        // tool.global.ui = org.apache.turbine.util.pull.UIManager
281        // tool.global.mm = org.apache.turbine.util.pull.MessageManager
282        //
283        // tool.request.link = org.apache.turbine.services.pull.tools.TemplateLink
284        //
285        // tool.session.basket = org.sample.util.ShoppingBasket;
286        //
287        // tool.persistent.ui = org.apache.turbine.services.pull.util.PersistentUIManager
288
289        log.debug("Global Tools:");
290        globalTools     = getTools(conf.subset(GLOBAL_TOOL));
291        log.debug("Request Tools:");
292        requestTools    = getTools(conf.subset(REQUEST_TOOL));
293        log.debug("Session Tools:");
294        sessionTools    = getTools(conf.subset(SESSION_TOOL));
295        log.debug("Authorized Tools:");
296        authorizedTools = getTools(conf.subset(AUTHORIZED_TOOL));
297        log.debug("Persistent Tools:");
298        persistentTools = getTools(conf.subset(PERSISTENT_TOOL));
299
300        // Create and populate the global context right now
301
302        // This is unholy, because it entwines the VelocityService and
303        // the Pull Service even further. However, there isn't much we can
304        // do for the 2.3 release. Expect this to go post-2.3
305        globalContext = velocity.getNewContext();
306
307        populateWithGlobalTools(globalContext);
308    }
309
310    /**
311     * Retrieve the tool names and classes for the tools defined
312     * in the configuration file with the prefix given.
313     *
314     * @param toolConfig The part of the configuration describing some tools
315     */
316    @SuppressWarnings("unchecked")
317    private List<ToolData> getTools(Configuration toolConfig)
318    {
319        List<ToolData> tools = new ArrayList<ToolData>();
320
321        // There might not be any tools for this prefix
322        // so return an empty list.
323        if (toolConfig == null)
324        {
325            return tools;
326        }
327
328        for (Iterator<String> it = toolConfig.getKeys(); it.hasNext();)
329        {
330            String toolName = it.next();
331            String toolClassName = toolConfig.getString(toolName);
332
333            try
334            {
335                // Create an instance of the tool class.
336                Class<ApplicationTool> toolClass = (Class<ApplicationTool>) Class.forName(toolClassName);
337
338                // Add the tool to the list being built.
339                tools.add(new ToolData(toolName, toolClassName, toolClass));
340
341                log.info("Tool " + toolClassName
342                    + " to add to the context as '$" + toolName + "'");
343            }
344            // NoClassDefFoundError + ClassNotFoundException
345            catch (Throwable 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                    // hashmap
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        if (globalTools != null)
942        {
943            for (ToolData toolData : globalTools)
944            {
945                Object tool = globalContext.get(toolData.toolName);
946                refreshTool(tool, null);
947            }
948        }
949    }
950
951    /**
952     * Release the request-scope tool instances in the
953     * given Context back to the pool
954     *
955     * @param context the Velocity Context to release tools from
956     */
957    @Override
958    public void releaseTools(Context context)
959    {
960        // only the request tools can be released - other scoped
961        // tools will have continuing references to them
962        releaseTools(context, requestTools);
963    }
964
965    /**
966     * Release the given list of tools from the context back
967     * to the pool
968     *
969     * @param context the Context containing the tools
970     * @param tools a List of ToolData objects
971     */
972    private void releaseTools(Context context, List<ToolData> tools)
973    {
974        if (tools != null)
975        {
976            for (ToolData toolData : tools)
977            {
978                Object tool = context.remove(toolData.toolName);
979
980                if (tool != null)
981                {
982                    pool.putInstance(tool);
983                }
984            }
985        }
986    }
987
988    /**
989     * Initialized a given Tool with the passed init Object
990     *
991     * @param tool A Tool Object
992     * @param param The Init Parameter
993     *
994     * @throws Exception If anything went wrong.
995     */
996    private void initTool(Object tool, Object param)
997        throws Exception
998    {
999        AnnotationProcessor.process(tool);
1000
1001        if (param instanceof PipelineData)
1002        {
1003            if (tool instanceof PipelineDataApplicationTool)
1004            {
1005                ((PipelineDataApplicationTool) tool).init(param);
1006            }
1007            else if (tool instanceof RunDataApplicationTool)
1008            {
1009                RunData data = getRunData((PipelineData)param);
1010                ((RunDataApplicationTool) tool).init(data);
1011            }
1012            else if (tool instanceof ApplicationTool)
1013            {
1014                RunData data = getRunData((PipelineData)param);
1015                ((ApplicationTool) tool).init(data);
1016            }
1017        }
1018        else
1019        {
1020            if (tool instanceof PipelineDataApplicationTool)
1021            {
1022                ((PipelineDataApplicationTool) tool).init(param);
1023            }
1024            else if (tool instanceof RunDataApplicationTool)
1025            {
1026                ((RunDataApplicationTool) tool).init(param);
1027            }
1028            else if (tool instanceof ApplicationTool)
1029            {
1030                ((ApplicationTool) tool).init(param);
1031            }
1032        }
1033    }
1034
1035    /**
1036     * Refresh a given Tool.
1037     *
1038     * @param tool A Tool Object
1039     * @param pipelineData The current RunData Object
1040     */
1041    private void refreshTool(Object tool, Object dataObject)
1042    {
1043        RunData data = null;
1044        PipelineData pipelineData = null;
1045        if (dataObject instanceof PipelineData)
1046        {
1047            pipelineData = (PipelineData)dataObject;
1048            data = getRunData(pipelineData);
1049            if (tool instanceof PipelineDataApplicationTool)
1050            {
1051                ((PipelineDataApplicationTool) tool).refresh(pipelineData);
1052            }
1053        }
1054        if (tool instanceof ApplicationTool)
1055        {
1056            ((ApplicationTool) tool).refresh();
1057        }
1058        else if (tool instanceof RunDataApplicationTool)
1059        {
1060            ((RunDataApplicationTool) tool).refresh(data);
1061        }
1062    }
1063
1064    private RunData getRunData(PipelineData pipelineData)
1065    {
1066        if(!(pipelineData instanceof RunData)){
1067            throw new RuntimeException("Can't cast to rundata from pipeline data.");
1068        }
1069        return (RunData)pipelineData;
1070    }
1071}