    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}