    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    }