Coverage Report - org.apache.turbine.services.pull.TurbinePullService
 
Classes in this File Line Coverage Branch Coverage Complexity
TurbinePullService
72%
134/184
55%
41/74
3,35
TurbinePullService$ToolData
100%
5/5
N/A
3,35
 
 1  
 package org.apache.turbine.services.pull;
 2  
 
 3  
 
 4  
 /*
 5  
  * Licensed to the Apache Software Foundation (ASF) under one
 6  
  * or more contributor license agreements.  See the NOTICE file
 7  
  * distributed with this work for additional information
 8  
  * regarding copyright ownership.  The ASF licenses this file
 9  
  * to you under the Apache License, Version 2.0 (the
 10  
  * "License"); you may not use this file except in compliance
 11  
  * with the License.  You may obtain a copy of the License at
 12  
  *
 13  
  *   http://www.apache.org/licenses/LICENSE-2.0
 14  
  *
 15  
  * Unless required by applicable law or agreed to in writing,
 16  
  * software distributed under the License is distributed on an
 17  
  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 18  
  * KIND, either express or implied.  See the License for the
 19  
  * specific language governing permissions and limitations
 20  
  * under the License.
 21  
  */
 22  
 
 23  
 
 24  
 import java.util.ArrayList;
 25  
 import java.util.Iterator;
 26  
 import java.util.List;
 27  
 
 28  
 import org.apache.commons.configuration2.Configuration;
 29  
 import org.apache.fulcrum.pool.PoolService;
 30  
 import org.apache.fulcrum.security.model.turbine.TurbineUserManager;
 31  
 import org.apache.logging.log4j.LogManager;
 32  
 import org.apache.logging.log4j.Logger;
 33  
 import org.apache.turbine.Turbine;
 34  
 import org.apache.turbine.annotation.AnnotationProcessor;
 35  
 import org.apache.turbine.om.security.User;
 36  
 import org.apache.turbine.pipeline.PipelineData;
 37  
 import org.apache.turbine.services.InitializationException;
 38  
 import org.apache.turbine.services.TurbineBaseService;
 39  
 import org.apache.turbine.services.TurbineServices;
 40  
 import org.apache.turbine.services.velocity.VelocityService;
 41  
 import org.apache.turbine.util.RunData;
 42  
 import org.apache.velocity.context.Context;
 43  
 
 44  
 /**
 45  
  * This is the concrete implementation of the Turbine
 46  
  * Pull Service.
 47  
  * <p>
 48  
  * These are tools that are placed in the context by the service
 49  
  * These tools will be made available to all your
 50  
  * templates. You list the tools in the following way:
 51  
  * </p>
 52  
  * <pre>
 53  
  * tool.&lt;scope&gt;.&lt;id&gt; = &lt;classname&gt;
 54  
  *
 55  
  * &lt;scope&gt;      is the tool scope: global, request, session,
 56  
  *              authorized or persistent (see below for more details)
 57  
  * &lt;id&gt;         is the name of the tool in the context
 58  
  *
 59  
  * You can configure the tools in this way:
 60  
  * tool.&lt;id&gt;.&lt;parameter&gt; = &lt;value&gt;
 61  
  *
 62  
  * So if you find "global", "request", "session" or "persistent" as second
 63  
  * part, it is a configuration to put a tool into the toolbox, else it is a
 64  
  * tool specific configuration.
 65  
  *
 66  
  * For example:
 67  
  *
 68  
  * tool.global.ui    = org.apache.turbine.util.pull.UIManager
 69  
  * tool.global.mm    = org.apache.turbine.util.pull.MessageManager
 70  
  * tool.request.link = org.apache.turbine.services.pull.tools.TemplateLink
 71  
  * tool.request.page = org.apache.turbine.util.template.TemplatePageAttributes
 72  
  *
 73  
  * Then:
 74  
  *
 75  
  * tool.ui.skin = default
 76  
  *
 77  
  * configures the value of "skin" for the "ui" tool.
 78  
  *
 79  
  * Tools are accessible in all templates by the &lt;id&gt; given
 80  
  * to the tool. So for the above listings the UIManager would
 81  
  * be available as $ui, the MessageManager as $mm, the TemplateLink
 82  
  * as $link and the TemplatePageAttributes as $page.
 83  
  *
 84  
  * You should avoid using tool names called "global", "request",
 85  
  * "session" or "persistent" because of clashes with the possible Scopes.
 86  
  *
 87  
  * Scopes:
 88  
  *
 89  
  *  global:     tool is instantiated once and that instance is available
 90  
  *              to all templates for all requests. Tool must be threadsafe.
 91  
  *
 92  
  *  request:    tool is instantiated once for each request (although the
 93  
  *              PoolService is used to recycle instances). Tool need not
 94  
  *              be threadsafe.
 95  
  *
 96  
  *  session:    tool is instantiated once for each user session, and is
 97  
  *              stored in the session.  These tools do not need to be
 98  
  *              threadsafe.
 99  
  *
 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 1854797 2019-03-04 20:41:39Z tv $
 120  
  */
 121  99
 public class TurbinePullService
 122  
         extends TurbineBaseService
 123  
         implements PullService
 124  
 {
 125  
     /** Logging */
 126  99
     private static Logger log = LogManager.getLogger(TurbinePullService.class);
 127  
 
 128  
     /** Reference to the pool service */
 129  99
     private PoolService pool = null;
 130  
 
 131  
     /** Reference to the templating (nee Velocity) service */
 132  99
     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  420
         {
 157  420
             this.toolName = toolName;
 158  420
             this.toolClassName = toolClassName;
 159  420
             this.toolClass = toolClass;
 160  420
         }
 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  99
     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  120
                     pool = (PoolService)TurbineServices.getInstance().getService(PoolService.ROLE);
 194  
 
 195  120
             if (pool == null)
 196  
             {
 197  0
                 throw new InitializationException("Pull Service requires"
 198  
                     + " configured Pool Service!");
 199  
             }
 200  
 
 201  120
             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  120
             setInit(true);
 206  
 
 207  
             // Do _NOT_ move this before the setInit(true)
 208  120
             velocity = (VelocityService)TurbineServices.getInstance().getService(VelocityService.SERVICE_NAME);
 209  
 
 210  87
             if (velocity != null)
 211  
             {
 212  87
                 initPullTools();
 213  
             }
 214  
             else
 215  
             {
 216  0
                 log.info("Velocity Service not configured, skipping pull tools!");
 217  
             }
 218  
         }
 219  33
         catch (Exception e)
 220  
         {
 221  33
             throw new InitializationException("TurbinePullService failed to initialize", e);
 222  87
         }
 223  87
     }
 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  120
         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  120
         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  120
         refreshToolsPerRequest =
 245  120
             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  120
         if (refreshToolsPerRequest)
 252  
         {
 253  93
             log.info("Pull Model tools will be refreshed on a per request basis.");
 254  
         }
 255  120
     }
 256  
 
 257  
     /**
 258  
      * Initialize the pull tools. At this point, the
 259  
      * service must be marked as initialized, because the
 260  
      * tools may call the methods of this service via the
 261  
      * static facade class TurbinePull.
 262  
      *
 263  
      * @throws Exception A problem happened when starting up
 264  
      */
 265  
     private void initPullTools()
 266  
         throws Exception
 267  
     {
 268  
         // And for reasons I never really fully understood,
 269  
         // the tools directive is toplevel without the service
 270  
         // prefix. This is brain-damaged but for legacy reasons we
 271  
         // keep this. So this is the global turbine configuration:
 272  87
         Configuration conf = Turbine.getConfiguration();
 273  
 
 274  
         // Grab each list of tools that are to be used (for global scope,
 275  
         // request scope, authorized scope, session scope and persistent
 276  
         // scope tools). They are specified respectively in the TR.props
 277  
         // like this:
 278  
         //
 279  
         // tool.global.ui = org.apache.turbine.util.pull.UIManager
 280  
         // tool.global.mm = org.apache.turbine.util.pull.MessageManager
 281  
         //
 282  
         // tool.request.link = org.apache.turbine.services.pull.tools.TemplateLink
 283  
         //
 284  
         // tool.session.basket = org.sample.util.ShoppingBasket;
 285  
         //
 286  
         // tool.persistent.ui = org.apache.turbine.services.pull.util.PersistentUIManager
 287  
 
 288  87
         log.debug("Global Tools:");
 289  87
         globalTools     = getTools(conf.subset(GLOBAL_TOOL));
 290  87
         log.debug("Request Tools:");
 291  87
         requestTools    = getTools(conf.subset(REQUEST_TOOL));
 292  87
         log.debug("Session Tools:");
 293  87
         sessionTools    = getTools(conf.subset(SESSION_TOOL));
 294  87
         log.debug("Authorized Tools:");
 295  87
         authorizedTools = getTools(conf.subset(AUTHORIZED_TOOL));
 296  87
         log.debug("Persistent Tools:");
 297  87
         persistentTools = getTools(conf.subset(PERSISTENT_TOOL));
 298  
 
 299  
         // Create and populate the global context right now
 300  
 
 301  
         // This is unholy, because it entwines the VelocityService and
 302  
         // the Pull Service even further. However, there isn't much we can
 303  
         // do for the 2.3 release. Expect this to go post-2.3
 304  87
         globalContext = velocity.getNewContext();
 305  
 
 306  87
         populateWithGlobalTools(globalContext);
 307  87
     }
 308  
 
 309  
     /**
 310  
      * Retrieve the tool names and classes for the tools defined
 311  
      * in the configuration file with the prefix given.
 312  
      *
 313  
      * @param toolConfig The part of the configuration describing some tools
 314  
      */
 315  
     @SuppressWarnings("unchecked")
 316  
     private List<ToolData> getTools(Configuration toolConfig)
 317  
     {
 318  435
         List<ToolData> tools = new ArrayList<ToolData>();
 319  
 
 320  
         // There might not be any tools for this prefix
 321  
         // so return an empty list.
 322  435
         if (toolConfig == null)
 323  
         {
 324  0
             return tools;
 325  
         }
 326  
 
 327  435
         for (Iterator<String> it = toolConfig.getKeys(); it.hasNext();)
 328  
         {
 329  420
             String toolName = it.next();
 330  420
             String toolClassName = toolConfig.getString(toolName);
 331  
 
 332  
             try
 333  
             {
 334  
                 // Create an instance of the tool class.
 335  420
                 Class<ApplicationTool> toolClass = (Class<ApplicationTool>) Class.forName(toolClassName);
 336  
 
 337  
                 // Add the tool to the list being built.
 338  420
                 tools.add(new ToolData(toolName, toolClassName, toolClass));
 339  
 
 340  420
                 log.info("Tool {} to add to the context as '${}'", toolClassName, toolName);
 341  
             }
 342  0
             catch (NoClassDefFoundError | ClassNotFoundException e)
 343  
             {
 344  0
                 log.error("Cannot instantiate tool class {}", toolClassName, e);
 345  420
             }
 346  420
         }
 347  
 
 348  435
         return tools;
 349  
     }
 350  
 
 351  
     /**
 352  
      * Return the Context which contains all global tools that
 353  
      * are to be used in conjunction with the Turbine
 354  
      * Pull Model. The tools are refreshed every time the
 355  
      * global Context is pulled.
 356  
      */
 357  
     @Override
 358  
     public Context getGlobalContext()
 359  
     {
 360  30
         if (refreshToolsPerRequest)
 361  
         {
 362  30
             refreshGlobalTools();
 363  
         }
 364  30
         return globalContext;
 365  
     }
 366  
 
 367  
     /**
 368  
      * Populate the given context with all request, session, authorized
 369  
      * and persistent scope tools (it is assumed that the context
 370  
      * already wraps the global context, and thus already contains
 371  
      * the global tools).
 372  
      *
 373  
      * @param context a Velocity Context to populate
 374  
      * @param data a RunData object for request specific data
 375  
      */
 376  
     @Override
 377  
     public void populateContext(Context context, RunData data)
 378  
     {
 379  0
         populateWithRequestTools(context, data);
 380  
 
 381  
         // session tools (whether session-only or persistent are
 382  
         // very similar, so the same method is used - the
 383  
         // boolean parameter indicates whether get/setPerm is to be used
 384  
         // rather than get/setTemp)
 385  
 
 386  
         //
 387  
         // Session Tool start right at the session once the user has been set
 388  
         // while persistent and authorized Tools are started when the user has
 389  
         // logged in
 390  
         //
 391  0
         User user = data.getUser();
 392  
 
 393  
         // Note: Session tools are currently lost after the login action
 394  
         // because the anonymous user is replaced the the real user object.
 395  
         // We should either store the session pull tools in the session or
 396  
         // make Turbine.loginAction() copy the session pull tools into the
 397  
         // new user object.
 398  0
         populateWithSessionTools(sessionTools, context, data, user);
 399  
 
 400  
         TurbineUserManager userManager =
 401  
                 (TurbineUserManager)TurbineServices
 402  0
                         .getInstance()
 403  0
                         .getService(TurbineUserManager.ROLE);
 404  
 
 405  0
         if (!userManager.isAnonymousUser(user) && user.hasLoggedIn())
 406  
         {
 407  0
             populateWithSessionTools(authorizedTools, context, data, user);
 408  0
             populateWithPermTools(persistentTools, context, data, user);
 409  
         }
 410  0
     }
 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 pipelineData a PipelineData object for request specific data
 420  
      */
 421  
     @Override
 422  
     public void populateContext(Context context, PipelineData pipelineData)
 423  
     {
 424  27
         RunData data = pipelineData.getRunData();
 425  
 
 426  27
         populateWithRequestTools(context, pipelineData);
 427  
         // session tools (whether session-only or persistent are
 428  
         // very similar, so the same method is used - the
 429  
         // boolean parameter indicates whether get/setPerm is to be used
 430  
         // rather than get/setTemp)
 431  
 
 432  
         //
 433  
         // Session Tool start right at the session once the user has been set
 434  
         // while persistent and authorized Tools are started when the user has
 435  
         // logged in
 436  
         //
 437  27
         User user = data.getUser();
 438  
 
 439  
         // Note: Session tools are currently lost after the login action
 440  
         // because the anonymous user is replaced the the real user object.
 441  
         // We should either store the session pull tools in the session or
 442  
         // make Turbine.loginAction() copy the session pull tools into the
 443  
         // new user object.
 444  27
         populateWithSessionTools(sessionTools, context, data, user);
 445  
 
 446  
         TurbineUserManager userManager =
 447  
                 (TurbineUserManager)TurbineServices
 448  27
                         .getInstance()
 449  27
                         .getService(TurbineUserManager.ROLE);
 450  
 
 451  27
         if (!userManager.isAnonymousUser(user) && user.hasLoggedIn())
 452  
         {
 453  6
             populateWithSessionTools(authorizedTools, context, data, user);
 454  6
             populateWithPermTools(persistentTools, context, pipelineData, user);
 455  
         }
 456  27
     }
 457  
 
 458  
     /**
 459  
      * Populate the given context with the global tools
 460  
      *
 461  
      * @param context a Velocity Context to populate
 462  
      */
 463  
     private void populateWithGlobalTools(Context context)
 464  
     {
 465  87
         for (ToolData toolData : globalTools)
 466  
         {
 467  
             try
 468  
             {
 469  60
                 Object tool = toolData.toolClass.newInstance();
 470  
 
 471  
                 // global tools are init'd with a null data parameter
 472  60
                 initTool(tool, null);
 473  
 
 474  
                 // put the tool in the context
 475  60
                 context.put(toolData.toolName, tool);
 476  
             }
 477  0
             catch (Exception e)
 478  
             {
 479  0
                 log.error("Could not instantiate global tool {} from a {} object",
 480  
                     toolData.toolName, toolData.toolClassName, e);
 481  60
             }
 482  60
         }
 483  87
     }
 484  
 
 485  
     /**
 486  
      * Populate the given context with the request-scope tools
 487  
      *
 488  
      * @param context a Velocity Context to populate
 489  
      * @param data a RunData or PipelineData instance
 490  
      */
 491  
     private void populateWithRequestTools(Context context, Object data)
 492  
     {
 493  
         // Iterate the tools
 494  27
         for (ToolData toolData : requestTools)
 495  
         {
 496  
             try
 497  
             {
 498  
                 // Fetch Object through the Pool.
 499  135
                 Object tool = pool.getInstance(toolData.toolClass);
 500  
 
 501  
                 // request tools are init'd with a RunData object
 502  135
                 initTool(tool, data);
 503  
 
 504  
                 // put the tool in the context
 505  135
                 context.put(toolData.toolName, tool);
 506  
             }
 507  0
             catch (Exception e)
 508  
             {
 509  0
                 log.error("Could not instantiate request tool {} from a {} object",
 510  
                         toolData.toolName, toolData.toolClassName, e);
 511  135
             }
 512  135
         }
 513  27
     }
 514  
 
 515  
     /**
 516  
      * Populate the given context with the session-scoped tools.
 517  
      *
 518  
      * @param tools The list of tools with which to populate the session.
 519  
      * @param context The context to populate.
 520  
      * @param data The current RunData object
 521  
      * @param user The <code>User</code> object whose storage to
 522  
      * retrieve the tool from.
 523  
      */
 524  
     private void populateWithSessionTools(List<ToolData> tools, Context context,
 525  
             RunData data, User user)
 526  
     {
 527  
         // Iterate the tools
 528  33
         for (ToolData toolData : tools)
 529  
         {
 530  
             try
 531  
             {
 532  
                 // ensure that tool is created only once for a user
 533  
                 // by synchronizing against the user object
 534  27
                 synchronized (data.getSession())
 535  
                 {
 536  
                     // first try and fetch the tool from the user's
 537  
                     // hashmap
 538  27
                     Object tool = data.getSession().getAttribute(
 539  
                             SESSION_TOOLS_ATTRIBUTE_PREFIX
 540  
                             + toolData.toolClassName);
 541  
 
 542  27
                     if (tool == null)
 543  
                     {
 544  
                         // if not there, an instance must be fetched from
 545  
                         // the pool
 546  27
                         tool = pool.getInstance(toolData.toolClass);
 547  
 
 548  
                         // session tools are init'd with the User object
 549  27
                         initTool(tool, user);
 550  
                     }
 551  
 
 552  
                     // *NOT* else
 553  27
                     if(tool != null)
 554  
                     {
 555  
                         // store the newly created tool in the session
 556  54
                         data.getSession().setAttribute(
 557  
                                 SESSION_TOOLS_ATTRIBUTE_PREFIX
 558  27
                                 + tool.getClass().getName(), tool);
 559  
 
 560  
                         // This is a semantics change. In the old
 561  
                         // Turbine, Session tools were initialized and
 562  
                         // then refreshed every time they were pulled
 563  
                         // into the context if "refreshToolsPerRequest"
 564  
                         // was wanted.
 565  
                         //
 566  
                         // RunDataApplicationTools now have a parameter
 567  
                         // for refresh. If it is not refreshed immediately
 568  
                         // after init(), the parameter value will be undefined
 569  
                         // until the 2nd run. So we refresh all the session
 570  
                         // tools on every run, even if we just init'ed it.
 571  
                         //
 572  
 
 573  27
                         if (refreshToolsPerRequest)
 574  
                         {
 575  27
                             refreshTool(tool, data);
 576  
                         }
 577  
 
 578  
                         // put the tool in the context
 579  27
                         log.debug("Adding {} to ctx as {}", tool, toolData.toolName);
 580  27
                         context.put(toolData.toolName, tool);
 581  
                     }
 582  
                     else
 583  
                     {
 584  0
                         log.info("Tool {} was null, skipping it.", toolData.toolName);
 585  
                     }
 586  27
                 }
 587  
             }
 588  0
             catch (Exception e)
 589  
             {
 590  0
                 log.error("Could not instantiate session tool {} from a {} object",
 591  
                         toolData.toolName, toolData.toolClassName, e);
 592  27
             }
 593  27
         }
 594  33
     }
 595  
 
 596  
     /**
 597  
      * Populate the given context with the perm-scoped tools.
 598  
      *
 599  
      * @param tools The list of tools with which to populate the
 600  
      * session.
 601  
      * @param context The context to populate.
 602  
      * @param data The current RunData or PipelineData object
 603  
      * @param user The <code>User</code> object whose storage to
 604  
      * retrieve the tool from.
 605  
      */
 606  
     private void populateWithPermTools(List<ToolData> tools, Context context,
 607  
             Object data, User user)
 608  
     {
 609  
         // Iterate the tools
 610  6
         for (ToolData toolData : tools)
 611  
         {
 612  
             try
 613  
             {
 614  
                 // ensure that tool is created only once for a user
 615  
                 // by synchronizing against the user object
 616  0
                 synchronized (user)
 617  
                 {
 618  
                     // first try and fetch the tool from the user's
 619  
                     // hashtable
 620  0
                     Object tool = user.getPerm(toolData.toolClassName);
 621  
 
 622  0
                     if (tool == null)
 623  
                     {
 624  
                         // if not there, an instance must be fetched from
 625  
                         // the pool
 626  0
                         tool = pool.getInstance(toolData.toolClass);
 627  
 
 628  
                         // session tools are init'd with the User object
 629  0
                         initTool(tool, user);
 630  
 
 631  
                         // store the newly created tool in the user's hashtable
 632  0
                         user.setPerm(toolData.toolClassName, tool);
 633  
                     }
 634  
 
 635  
                     // *NOT* else
 636  0
                     if (tool != null)
 637  
                     {
 638  
                         // This is a semantics change. In the old
 639  
                         // Turbine, Session tools were initialized and
 640  
                         // then refreshed every time they were pulled
 641  
                         // into the context if "refreshToolsPerRequest"
 642  
                         // was wanted.
 643  
                         //
 644  
                         // RunDataApplicationTools now have a parameter
 645  
                         // for refresh. If it is not refreshed immediately
 646  
                         // after init(), the parameter value will be undefined
 647  
                         // until the 2nd run. So we refresh all the session
 648  
                         // tools on every run, even if we just init'ed it.
 649  
                         //
 650  
 
 651  0
                         if (refreshToolsPerRequest)
 652  
                         {
 653  0
                             refreshTool(tool, data);
 654  
                         }
 655  
 
 656  
                         // put the tool in the context
 657  0
                         log.debug("Adding {} to ctx as {}", tool, toolData.toolName);
 658  0
                         log.warn("Persistent scope tools are deprecated.");
 659  0
                         context.put(toolData.toolName, tool);
 660  
                     }
 661  
                     else
 662  
                     {
 663  0
                         log.info("Tool {} was null, skipping it.", toolData.toolName);
 664  
                     }
 665  0
                 }
 666  
             }
 667  0
             catch (Exception e)
 668  
             {
 669  0
                 log.error("Could not instantiate perm tool {} from a {} object",
 670  
                         toolData.toolName, toolData.toolClassName, e);
 671  0
             }
 672  0
         }
 673  6
     }
 674  
 
 675  
 
 676  
 
 677  
     /**
 678  
      * Return the absolute path to the resources directory
 679  
      * used by the application tools.
 680  
      *
 681  
      * @return the absolute path of the resources directory
 682  
      */
 683  
     @Override
 684  
     public String getAbsolutePathToResourcesDirectory()
 685  
     {
 686  0
         return Turbine.getRealPath(resourcesDirectory);
 687  
     }
 688  
 
 689  
     /**
 690  
      * Return the resources directory. This is
 691  
      * relative to the web context.
 692  
      *
 693  
      * @return the relative path of the resources directory
 694  
      */
 695  
     @Override
 696  
     public String getResourcesDirectory()
 697  
     {
 698  60
         return resourcesDirectory;
 699  
     }
 700  
 
 701  
     /**
 702  
      * Refresh the global tools. We can
 703  
      * only refresh those tools that adhere to
 704  
      * ApplicationTool interface because we
 705  
      * know those types of tools have a refresh
 706  
      * method.
 707  
      */
 708  
     private void refreshGlobalTools()
 709  
     {
 710  30
         if (globalTools != null)
 711  
         {
 712  30
             for (ToolData toolData : globalTools)
 713  
             {
 714  30
                 Object tool = globalContext.get(toolData.toolName);
 715  30
                 refreshTool(tool, null);
 716  30
             }
 717  
         }
 718  30
     }
 719  
 
 720  
     /**
 721  
      * Release the request-scope tool instances in the
 722  
      * given Context back to the pool
 723  
      *
 724  
      * @param context the Velocity Context to release tools from
 725  
      */
 726  
     @Override
 727  
     public void releaseTools(Context context)
 728  
     {
 729  
         // only the request tools can be released - other scoped
 730  
         // tools will have continuing references to them
 731  6
         releaseTools(context, requestTools);
 732  6
     }
 733  
 
 734  
     /**
 735  
      * Release the given list of tools from the context back
 736  
      * to the pool
 737  
      *
 738  
      * @param context the Context containing the tools
 739  
      * @param tools a List of ToolData objects
 740  
      */
 741  
     private void releaseTools(Context context, List<ToolData> tools)
 742  
     {
 743  6
         if (tools != null)
 744  
         {
 745  6
             for (ToolData toolData : tools)
 746  
             {
 747  30
                 Object tool = context.remove(toolData.toolName);
 748  
 
 749  30
                 if (tool != null)
 750  
                 {
 751  30
                     pool.putInstance(tool);
 752  
                 }
 753  30
             }
 754  
         }
 755  6
     }
 756  
 
 757  
     /**
 758  
      * Initialized a given Tool with the passed init Object
 759  
      *
 760  
      * @param tool A Tool Object
 761  
      * @param param The Init Parameter
 762  
      *
 763  
      * @throws Exception If anything went wrong.
 764  
      */
 765  
     private void initTool(Object tool, Object param)
 766  
         throws Exception
 767  
     {
 768  222
         AnnotationProcessor.process(tool);
 769  
 
 770  222
         if (param instanceof PipelineData)
 771  
         {
 772  135
             if (tool instanceof PipelineDataApplicationTool)
 773  
             {
 774  0
                 ((PipelineDataApplicationTool) tool).init(param);
 775  
             }
 776  135
             else if (tool instanceof RunDataApplicationTool)
 777  
             {
 778  0
                 RunData data = getRunData((PipelineData)param);
 779  0
                 ((RunDataApplicationTool) tool).init(data);
 780  0
             }
 781  135
             else if (tool instanceof ApplicationTool)
 782  
             {
 783  135
                 RunData data = getRunData((PipelineData)param);
 784  135
                 ((ApplicationTool) tool).init(data);
 785  135
             }
 786  
         }
 787  
         else
 788  
         {
 789  87
             if (tool instanceof PipelineDataApplicationTool)
 790  
             {
 791  0
                 ((PipelineDataApplicationTool) tool).init(param);
 792  
             }
 793  87
             else if (tool instanceof RunDataApplicationTool)
 794  
             {
 795  0
                 ((RunDataApplicationTool) tool).init(param);
 796  
             }
 797  87
             else if (tool instanceof ApplicationTool)
 798  
             {
 799  87
                 ((ApplicationTool) tool).init(param);
 800  
             }
 801  
         }
 802  222
     }
 803  
 
 804  
     /**
 805  
      * Refresh a given Tool.
 806  
      *
 807  
      * @param tool A Tool Object
 808  
      * @param pipelineData The current RunData Object
 809  
      */
 810  
     private void refreshTool(Object tool, Object dataObject)
 811  
     {
 812  57
         RunData data = null;
 813  57
         PipelineData pipelineData = null;
 814  57
         if (dataObject instanceof PipelineData)
 815  
         {
 816  27
             pipelineData = (PipelineData)dataObject;
 817  27
             data = pipelineData.getRunData();
 818  27
             if (tool instanceof PipelineDataApplicationTool)
 819  
             {
 820  0
                 ((PipelineDataApplicationTool) tool).refresh(pipelineData);
 821  
             }
 822  
         }
 823  57
         if (tool instanceof ApplicationTool)
 824  
         {
 825  57
             ((ApplicationTool) tool).refresh();
 826  
         }
 827  0
         else if (tool instanceof RunDataApplicationTool)
 828  
         {
 829  0
             ((RunDataApplicationTool) tool).refresh(data);
 830  
         }
 831  57
     }
 832  
 
 833  
     private RunData getRunData(PipelineData pipelineData)
 834  
     {
 835  135
         if (!(pipelineData instanceof RunData))
 836  
         {
 837  0
             throw new RuntimeException("Can't cast to rundata from pipeline data.");
 838  
         }
 839  135
         return (RunData)pipelineData;
 840  
     }
 841  
 }