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