1 package org.apache.turbine.services.pull;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 import java.util.ArrayList;
20 import java.util.Iterator;
21 import java.util.List;
22
23 import org.apache.commons.configuration.Configuration;
24
25 import org.apache.commons.logging.Log;
26 import org.apache.commons.logging.LogFactory;
27
28 import org.apache.turbine.Turbine;
29 import org.apache.turbine.om.security.User;
30 import org.apache.turbine.services.InitializationException;
31 import org.apache.turbine.services.TurbineBaseService;
32 import org.apache.turbine.services.pool.PoolService;
33 import org.apache.turbine.services.pool.TurbinePool;
34 import org.apache.turbine.services.security.TurbineSecurity;
35 import org.apache.turbine.services.velocity.VelocityService;
36 import org.apache.turbine.services.velocity.TurbineVelocity;
37 import org.apache.turbine.util.RunData;
38
39 import org.apache.velocity.context.Context;
40
41 /***
42 * This is the concrete implementation of the Turbine
43 * Pull Service.
44 * <p>
45 * These are tools that are placed in the context by the service
46 * These tools will be made available to all your
47 * templates. You list the tools in the following way:
48 * <p>
49 * <pre>
50 * tool.<scope>.<id> = <classname>
51 *
52 * <scope> is the tool scope: global, request, session,
53 * authorized or persistent (see below for more details)
54 * <id> is the name of the tool in the context
55 *
56 * You can configure the tools in this way:
57 * tool.<id>.<parameter> = <value>
58 *
59 * So if you find "global", "request", "session" or "persistent" as second
60 * part, it is a configuration to put a tool into the toolbox, else it is a
61 * tool specific configuration.
62 *
63 * For example:
64 *
65 * tool.global.ui = org.apache.turbine.util.pull.UIManager
66 * tool.global.mm = org.apache.turbine.util.pull.MessageManager
67 * tool.request.link = org.apache.turbine.services.pull.tools.TemplateLink
68 * tool.request.page = org.apache.turbine.util.template.TemplatePageAttributes
69 *
70 * Then:
71 *
72 * tool.ui.skin = default
73 *
74 * configures the value of "skin" for the "ui" tool.
75 *
76 * Tools are accessible in all templates by the <id> given
77 * to the tool. So for the above listings the UIManager would
78 * be available as $ui, the MessageManager as $mm, the TemplateLink
79 * as $link and the TemplatePageAttributes as $page.
80 *
81 * You should avoid using tool names called "global", "request",
82 * "session" or "persistent" because of clashes with the possible Scopes.
83 *
84 * Scopes:
85 *
86 * global: tool is instantiated once and that instance is available
87 * to all templates for all requests. Tool must be threadsafe.
88 *
89 * request: tool is instantiated once for each request (although the
90 * PoolService is used to recycle instances). Tool need not
91 * be threadsafe.
92 *
93 * session: tool is instantiated once for each user session, and is
94 * stored in the session. These tools do not need to be
95 * threadsafe.
96 *
97 * authorized: tool is instantiated once for each user session once the
98 * user logs in. After this, it is a normal session tool.
99 *
100 * persistent: tool is instantitated once for each user session once
101 * the user logs in and is is stored in the user's permanent
102 * hashtable.
103 * This means for a logged in user the tool will be persisted
104 * in the user's objectdata. Tool should be Serializable. These
105 * tools do not need to be threadsafe.
106 * <b>persistent scope tools are deprecated in 2.3</b>
107 *
108 * Defaults: none
109 * </pre>
110 *
111 * @author <a href="mailto:jvanzyl@periapt.com">Jason van Zyl</a>
112 * @author <a href="mailto:sean@informage.net">Sean Legassick</a>
113 * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
114 * @author <a href="mailto:quintonm@bellsouth.net">Quinton McCombs</a>
115 * @version $Id: TurbinePullService.java 264148 2005-08-29 14:21:04Z henning $
116 */
117 public class TurbinePullService
118 extends TurbineBaseService
119 implements PullService
120 {
121 /*** Logging */
122 private static Log log = LogFactory.getLog(TurbinePullService.class);
123
124 /*** Reference to the pool service */
125 private PoolService pool = null;
126
127 /*** Reference to the templating (nee Velocity) service */
128 private VelocityService velocity = null;
129
130 /***
131 * This is the container for the global web application
132 * tools that are used in conjunction with the
133 * Turbine Pull Model. All the global tools will be placed
134 * in this Context and be made accessible inside
135 * templates via the tool name specified in the TR.props
136 * file.
137 */
138 private Context globalContext;
139
140 /***
141 * This inner class is used in the lists below to store the
142 * tool name and class for each of request, session and persistent
143 * tools
144 */
145 private static class ToolData
146 {
147 String toolName;
148 String toolClassName;
149 Class toolClass;
150
151 public ToolData(String toolName, String toolClassName, Class toolClass)
152 {
153 this.toolName = toolName;
154 this.toolClassName = toolClassName;
155 this.toolClass = toolClass;
156 }
157 }
158
159 /*** Internal list of global tools */
160 private List globalTools;
161
162 /*** Internal list of request tools */
163 private List requestTools;
164
165 /*** Internal list of session tools */
166 private List sessionTools;
167
168 /*** Internal list of authorized tools */
169 private List authorizedTools;
170
171 /*** Internal list of persistent tools */
172 private List persistentTools;
173
174 /*** Directory where application tool resources are stored.*/
175 private String resourcesDirectory;
176
177 /*** Should we refresh the application tools on a per request basis? */
178 private boolean refreshToolsPerRequest = false;
179
180 /***
181 * Called the first time the Service is used.
182 */
183 public void init()
184 throws InitializationException
185 {
186 try
187 {
188 pool = TurbinePool.getService();
189
190 if (pool == null)
191 {
192 throw new InitializationException("Pull Service requires"
193 + " configured Pool Service!");
194 }
195
196 initPullService();
197
198
199
200 setInit(true);
201
202
203 velocity = TurbineVelocity.getService();
204
205 if (velocity != null)
206 {
207 initPullTools();
208 }
209 else
210 {
211 log.info("Velocity Service not configured, skipping pull tools!");
212 }
213 }
214 catch (Exception e)
215 {
216 throw new InitializationException(
217 "TurbinePullService failed to initialize", e);
218 }
219 }
220
221 /***
222 * Initialize the pull service
223 *
224 * @exception Exception A problem happened when starting up
225 */
226 private void initPullService()
227 throws Exception
228 {
229
230 Configuration conf = getConfiguration();
231
232
233
234 resourcesDirectory = conf.getString(
235 TOOL_RESOURCES_DIR_KEY,
236 TOOL_RESOURCES_DIR_DEFAULT);
237
238
239
240 refreshToolsPerRequest =
241 conf.getBoolean(
242 TOOLS_PER_REQUEST_REFRESH_KEY,
243 TOOLS_PER_REQUEST_REFRESH_DEFAULT);
244
245
246
247 if (refreshToolsPerRequest)
248 {
249 log.info("Pull Model tools will "
250 + "be refreshed on a per request basis.");
251 }
252 }
253
254 /***
255 * Initialize the pull tools. At this point, the
256 * service must be marked as initialized, because the
257 * tools may call the methods of this service via the
258 * static facade class TurbinePull.
259 *
260 * @exception Exception A problem happened when starting up
261 */
262 private void initPullTools()
263 throws Exception
264 {
265
266
267
268
269 Configuration conf = Turbine.getConfiguration();
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285 log.debug("Global Tools:");
286 globalTools = getTools(conf.subset(GLOBAL_TOOL));
287 log.debug("Request Tools:");
288 requestTools = getTools(conf.subset(REQUEST_TOOL));
289 log.debug("Session Tools:");
290 sessionTools = getTools(conf.subset(SESSION_TOOL));
291 log.debug("Authorized Tools:");
292 authorizedTools = getTools(conf.subset(AUTHORIZED_TOOL));
293 log.debug("Persistent Tools:");
294 persistentTools = getTools(conf.subset(PERSISTENT_TOOL));
295
296
297
298
299
300
301 globalContext = velocity.getNewContext();
302
303 populateWithGlobalTools(globalContext);
304 }
305
306 /***
307 * Retrieve the tool names and classes for the tools definied
308 * in the configuration file with the prefix given.
309 *
310 * @param toolConfig The part of the configuration describing some tools
311 */
312 private List getTools(Configuration toolConfig)
313 {
314 List tools = new ArrayList();
315
316
317
318 if (toolConfig == null)
319 {
320 return tools;
321 }
322
323 for (Iterator it = toolConfig.getKeys(); it.hasNext();)
324 {
325 String toolName = (String) it.next();
326 String toolClassName = toolConfig.getString(toolName);
327
328 try
329 {
330
331 Class toolClass = Class.forName(toolClassName);
332
333
334 tools.add(new ToolData(toolName, toolClassName, toolClass));
335
336 log.info("Tool " + toolClassName
337 + " to add to the context as '$" + toolName + "'");
338 }
339 catch (Exception e)
340 {
341 log.error("Cannot instantiate tool class "
342 + toolClassName + ": ", e);
343 }
344 }
345
346 return tools;
347 }
348
349 /***
350 * Return the Context which contains all global tools that
351 * are to be used in conjunction with the Turbine
352 * Pull Model. The tools are refreshed every time the
353 * global Context is pulled.
354 */
355 public Context getGlobalContext()
356 {
357 if (refreshToolsPerRequest)
358 {
359 refreshGlobalTools();
360 }
361 return globalContext;
362 }
363
364 /***
365 * Populate the given context with all request, session, authorized
366 * and persistent scope tools (it is assumed that the context
367 * already wraps the global context, and thus already contains
368 * the global tools).
369 *
370 * @param context a Velocity Context to populate
371 * @param data a RunData object for request specific data
372 */
373 public void populateContext(Context context, RunData data)
374 {
375 populateWithRequestTools(context, data);
376
377
378
379
380
381
382
383
384
385
386
387 User user = data.getUser();
388
389
390
391
392
393
394 populateWithSessionTools(sessionTools, context, data, user);
395
396 if (!TurbineSecurity.isAnonymousUser(user))
397 {
398 if (user.hasLoggedIn())
399 {
400 populateWithSessionTools(authorizedTools, context, data, user);
401 populateWithPermTools(persistentTools, context, data, user);
402 }
403 }
404 }
405
406 /***
407 * Populate the given context with the global tools
408 *
409 * @param context a Velocity Context to populate
410 */
411 private void populateWithGlobalTools(Context context)
412 {
413 for (Iterator it = globalTools.iterator(); it.hasNext();)
414 {
415 ToolData toolData = (ToolData) it.next();
416 try
417 {
418 Object tool = toolData.toolClass.newInstance();
419
420
421 initTool(tool, null);
422
423
424 context.put(toolData.toolName, tool);
425 }
426 catch (Exception e)
427 {
428 log.error("Could not instantiate global tool "
429 + toolData.toolName + " from a "
430 + toolData.toolClassName + " object", e);
431 }
432 }
433 }
434
435 /***
436 * Populate the given context with the request-scope tools
437 *
438 * @param context a Velocity Context to populate
439 * @param data a RunData instance
440 */
441 private void populateWithRequestTools(Context context, RunData data)
442 {
443
444 for (Iterator it = requestTools.iterator(); it.hasNext();)
445 {
446 ToolData toolData = (ToolData) it.next();
447 try
448 {
449
450 Object tool = pool.getInstance(toolData.toolClass);
451
452
453 initTool(tool, data);
454
455
456 context.put(toolData.toolName, tool);
457 }
458 catch (Exception e)
459 {
460 log.error("Could not instantiate request tool "
461 + toolData.toolName + " from a "
462 + toolData.toolClassName + " object", e);
463 }
464 }
465 }
466
467 /***
468 * Populate the given context with the session-scoped tools.
469 *
470 * @param tools The list of tools with which to populate the
471 * session.
472 * @param context The context to populate.
473 * @param data The current RunData object
474 * @param user The <code>User</code> object whose storage to
475 * retrieve the tool from.
476 */
477 private void populateWithSessionTools(List tools, Context context,
478 RunData data, User user)
479 {
480
481 for (Iterator it = tools.iterator(); it.hasNext();)
482 {
483 ToolData toolData = (ToolData) it.next();
484 try
485 {
486
487
488 synchronized (data.getSession())
489 {
490
491
492 Object tool = data.getSession().getAttribute(
493 SESSION_TOOLS_ATTRIBUTE_PREFIX
494 + toolData.toolClassName);
495
496 if (tool == null)
497 {
498
499
500 tool = pool.getInstance(toolData.toolClass);
501
502
503 initTool(tool, user);
504
505
506 data.getSession().setAttribute(
507 SESSION_TOOLS_ATTRIBUTE_PREFIX
508 + tool.getClass().getName(), tool);
509 }
510
511
512 if(tool != null)
513 {
514
515
516
517
518
519
520
521
522
523
524
525
526
527 if (refreshToolsPerRequest)
528 {
529 refreshTool(tool, data);
530 }
531
532
533 log.debug("Adding " + tool + " to ctx as "
534 + toolData.toolName);
535 context.put(toolData.toolName, tool);
536 }
537 else
538 {
539 log.info("Tool " + toolData.toolName
540 + " was null, skipping it.");
541 }
542 }
543 }
544 catch (Exception e)
545 {
546 log.error("Could not instantiate session tool "
547 + toolData.toolName + " from a "
548 + toolData.toolClassName + " object", e);
549 }
550 }
551 }
552
553 /***
554 * Populate the given context with the perm-scoped tools.
555 *
556 * @param tools The list of tools with which to populate the
557 * session.
558 * @param context The context to populate.
559 * @param data The current RunData object
560 * @param user The <code>User</code> object whose storage to
561 * retrieve the tool from.
562 */
563 private void populateWithPermTools(List tools, Context context,
564 RunData data, User user)
565 {
566
567 for (Iterator it = tools.iterator(); it.hasNext();)
568 {
569 ToolData toolData = (ToolData) it.next();
570 try
571 {
572
573
574 synchronized (user)
575 {
576
577
578 Object tool = user.getPerm(toolData.toolClassName);
579
580 if (tool == null)
581 {
582
583
584 tool = pool.getInstance(toolData.toolClass);
585
586
587 initTool(tool, user);
588
589
590 user.setPerm(toolData.toolClassName, tool);
591 }
592
593
594 if(tool != null)
595 {
596
597
598
599
600
601
602
603
604
605
606
607
608
609 if (refreshToolsPerRequest)
610 {
611 refreshTool(tool, data);
612 }
613
614
615 log.debug("Adding " + tool + " to ctx as "
616 + toolData.toolName);
617 log.warn("Persistent scope tools are deprecated.");
618 context.put(toolData.toolName, tool);
619 }
620 else
621 {
622 log.info("Tool " + toolData.toolName
623 + " was null, skipping it.");
624 }
625 }
626 }
627 catch (Exception e)
628 {
629 log.error("Could not instantiate perm tool "
630 + toolData.toolName + " from a "
631 + toolData.toolClassName + " object", e);
632 }
633 }
634 }
635
636 /***
637 * Return the absolute path to the resources directory
638 * used by the application tools.
639 *
640 * @return the absolute path of the resources directory
641 */
642 public String getAbsolutePathToResourcesDirectory()
643 {
644 return Turbine.getRealPath(resourcesDirectory);
645 }
646
647 /***
648 * Return the resources directory. This is
649 * relative to the web context.
650 *
651 * @return the relative path of the resources directory
652 */
653 public String getResourcesDirectory()
654 {
655 return resourcesDirectory;
656 }
657
658 /***
659 * Refresh the global tools. We can
660 * only refresh those tools that adhere to
661 * ApplicationTool interface because we
662 * know those types of tools have a refresh
663 * method.
664 * @deprecated Will be made private after 2.3
665 */
666 public void refreshGlobalTools()
667 {
668 for (Iterator it = globalTools.iterator(); it.hasNext();)
669 {
670 ToolData toolData = (ToolData) it.next();
671 Object tool = globalContext.get(toolData.toolName);
672 refreshTool(tool, null);
673 }
674 }
675
676 /***
677 * Should we refresh the ToolBox on
678 * a per request basis.
679 * @deprecated No longer needed as Pull and Velocity Service are now more separate.
680 */
681 public boolean refreshToolsPerRequest()
682 {
683 return refreshToolsPerRequest;
684 }
685
686 /***
687 * Release the request-scope tool instances in the
688 * given Context back to the pool
689 *
690 * @param context the Velocity Context to release tools from
691 */
692 public void releaseTools(Context context)
693 {
694
695
696 releaseTools(context, requestTools);
697 }
698
699 /***
700 * Release the given list of tools from the context back
701 * to the pool
702 *
703 * @param context the Context containing the tools
704 * @param tools a List of ToolData objects
705 */
706 private void releaseTools(Context context, List tools)
707 {
708 for (Iterator it = tools.iterator(); it.hasNext();)
709 {
710 ToolData toolData = (ToolData) it.next();
711 Object tool = context.remove(toolData.toolName);
712
713 if (tool != null)
714 {
715 pool.putInstance(tool);
716 }
717 }
718 }
719
720 /***
721 * Initialized a given Tool with the passed init Object
722 *
723 * @param tool A Tool Object
724 * @param param The Init Parameter
725 *
726 * @throws Exception If anything went wrong.
727 */
728 private void initTool(Object tool, Object param)
729 throws Exception
730 {
731 if (tool instanceof ApplicationTool)
732 {
733 ((ApplicationTool) tool).init(param);
734 }
735 else if (tool instanceof RunDataApplicationTool)
736 {
737 ((RunDataApplicationTool) tool).init(param);
738 }
739 }
740
741 /***
742 * Refresh a given Tool.
743 *
744 * @param tool A Tool Object
745 * @param data The current RunData Object
746 */
747 private void refreshTool(Object tool, RunData data)
748 {
749 if (tool instanceof ApplicationTool)
750 {
751 ((ApplicationTool) tool).refresh();
752 }
753 else if (tool instanceof RunDataApplicationTool)
754 {
755 ((RunDataApplicationTool) tool).refresh(data);
756 }
757 }
758 }