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.<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.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 <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 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$
120 */
121 public class TurbinePullService
122 extends TurbineBaseService
123 implements PullService
124 {
125 /** Logging */
126 private static Logger log = LogManager.getLogger(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 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 * @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 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 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 // 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 globalContext = velocity.getNewContext();
305
306 populateWithGlobalTools(globalContext);
307 }
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 List<ToolData> tools = new ArrayList<>();
319
320 // There might not be any tools for this prefix
321 // so return an empty list.
322 if (toolConfig == null)
323 {
324 return tools;
325 }
326
327 for (Iterator<String> it = toolConfig.getKeys(); it.hasNext();)
328 {
329 String toolName = it.next();
330 String toolClassName = toolConfig.getString(toolName);
331
332 try
333 {
334 // Create an instance of the tool class.
335 Class<ApplicationTool> toolClass = (Class<ApplicationTool>) Class.forName(toolClassName);
336
337 // Add the tool to the list being built.
338 tools.add(new ToolData(toolName, toolClassName, toolClass));
339
340 log.info("Tool {} to add to the context as '${}'", toolClassName, toolName);
341 }
342 catch (NoClassDefFoundError | ClassNotFoundException e)
343 {
344 log.error("Cannot instantiate tool class {}", toolClassName, e);
345 }
346 }
347
348 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 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 @Override
377 public void populateContext(Context context, RunData data)
378 {
379 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 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 populateWithSessionTools(sessionTools, context, data, user);
399
400 TurbineUserManager userManager =
401 (TurbineUserManager)TurbineServices
402 .getInstance()
403 .getService(TurbineUserManager.ROLE);
404
405 if (!userManager.isAnonymousUser(user) && user.hasLoggedIn())
406 {
407 populateWithSessionTools(authorizedTools, context, data, user);
408 populateWithPermTools(persistentTools, context, data, user);
409 }
410 }
411
412 /**
413 * Populate the given context with all request, session, authorized
414 * and persistent scope tools (it is assumed that the context
415 * already wraps the global context, and thus already contains
416 * the global tools).
417 *
418 * @param context a Velocity Context to populate
419 * @param pipelineData a PipelineData object for request specific data
420 */
421 @Override
422 public void populateContext(Context context, PipelineData pipelineData)
423 {
424 RunData data = pipelineData.getRunData();
425
426 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 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 populateWithSessionTools(sessionTools, context, data, user);
445
446 TurbineUserManager userManager =
447 (TurbineUserManager)TurbineServices
448 .getInstance()
449 .getService(TurbineUserManager.ROLE);
450
451 if (!userManager.isAnonymousUser(user) && user.hasLoggedIn())
452 {
453 populateWithSessionTools(authorizedTools, context, data, user);
454 populateWithPermTools(persistentTools, context, pipelineData, user);
455 }
456 }
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 for (ToolData toolData : globalTools)
466 {
467 try
468 {
469 Object tool = toolData.toolClass.getDeclaredConstructor().newInstance();
470
471 // global tools are init'd with a null data parameter
472 initTool(tool, null);
473
474 // put the tool in the context
475 context.put(toolData.toolName, tool);
476 }
477 catch (Exception e)
478 {
479 log.error("Could not instantiate global tool {} from a {} object",
480 toolData.toolName, toolData.toolClassName, e);
481 }
482 }
483 }
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 for (ToolData toolData : requestTools)
495 {
496 try
497 {
498 // Fetch Object through the Pool.
499 Object tool = pool.getInstance(toolData.toolClass);
500
501 // request tools are init'd with a RunData object
502 initTool(tool, data);
503
504 // put the tool in the context
505 context.put(toolData.toolName, tool);
506 }
507 catch (Exception e)
508 {
509 log.error("Could not instantiate request tool {} from a {} object",
510 toolData.toolName, toolData.toolClassName, e);
511 }
512 }
513 }
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 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 synchronized (data.getSession())
535 {
536 // first try and fetch the tool from the user's
537 // hashmap
538 Object tool = data.getSession().getAttribute(
539 SESSION_TOOLS_ATTRIBUTE_PREFIX
540 + toolData.toolClassName);
541
542 if (tool == null)
543 {
544 // if not there, an instance must be fetched from
545 // the pool
546 tool = pool.getInstance(toolData.toolClass);
547
548 // session tools are init'd with the User object
549 initTool(tool, user);
550 }
551
552 // *NOT* else
553 if(tool != null)
554 {
555 // store the newly created tool in the session
556 data.getSession().setAttribute(
557 SESSION_TOOLS_ATTRIBUTE_PREFIX
558 + 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 if (refreshToolsPerRequest)
574 {
575 refreshTool(tool, data);
576 }
577
578 // put the tool in the context
579 log.debug("Adding {} to ctx as {}", tool, toolData.toolName);
580 context.put(toolData.toolName, tool);
581 }
582 else
583 {
584 log.info("Tool {} was null, skipping it.", toolData.toolName);
585 }
586 }
587 }
588 catch (Exception e)
589 {
590 log.error("Could not instantiate session tool {} from a {} object",
591 toolData.toolName, toolData.toolClassName, e);
592 }
593 }
594 }
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 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 synchronized (user)
617 {
618 // first try and fetch the tool from the user's
619 // hashtable
620 Object tool = user.getPerm(toolData.toolClassName);
621
622 if (tool == null)
623 {
624 // if not there, an instance must be fetched from
625 // the pool
626 tool = pool.getInstance(toolData.toolClass);
627
628 // session tools are init'd with the User object
629 initTool(tool, user);
630
631 // store the newly created tool in the user's hashtable
632 user.setPerm(toolData.toolClassName, tool);
633 }
634
635 // *NOT* else
636 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 if (refreshToolsPerRequest)
652 {
653 refreshTool(tool, data);
654 }
655
656 // put the tool in the context
657 log.debug("Adding {} to ctx as {}", tool, toolData.toolName);
658 log.warn("Persistent scope tools are deprecated.");
659 context.put(toolData.toolName, tool);
660 }
661 else
662 {
663 log.info("Tool {} was null, skipping it.", toolData.toolName);
664 }
665 }
666 }
667 catch (Exception e)
668 {
669 log.error("Could not instantiate perm tool {} from a {} object",
670 toolData.toolName, toolData.toolClassName, e);
671 }
672 }
673 }
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 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 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 if (globalTools != null)
711 {
712 for (ToolData toolData : globalTools)
713 {
714 Object tool = globalContext.get(toolData.toolName);
715 refreshTool(tool, null);
716 }
717 }
718 }
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 releaseTools(context, requestTools);
732 }
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 if (tools != null)
744 {
745 for (ToolData toolData : tools)
746 {
747 Object tool = context.remove(toolData.toolName);
748
749 if (tool != null)
750 {
751 pool.putInstance(tool);
752 }
753 }
754 }
755 }
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 AnnotationProcessor.process(tool);
769
770 if (param instanceof PipelineData)
771 {
772 if (tool instanceof PipelineDataApplicationTool)
773 {
774 ((PipelineDataApplicationTool) tool).init(param);
775 }
776 else if (tool instanceof RunDataApplicationTool)
777 {
778 RunData data = getRunData((PipelineData)param);
779 ((RunDataApplicationTool) tool).init(data);
780 }
781 else if (tool instanceof ApplicationTool)
782 {
783 RunData data = getRunData((PipelineData)param);
784 ((ApplicationTool) tool).init(data);
785 }
786 }
787 else
788 {
789 if (tool instanceof PipelineDataApplicationTool)
790 {
791 ((PipelineDataApplicationTool) tool).init(param);
792 }
793 else if (tool instanceof RunDataApplicationTool)
794 {
795 ((RunDataApplicationTool) tool).init(param);
796 }
797 else if (tool instanceof ApplicationTool)
798 {
799 ((ApplicationTool) tool).init(param);
800 }
801 }
802 }
803
804 /**
805 * Refresh a given Tool.
806 *
807 * @param tool A Tool Object
808 * @param dataObject The current RunData Object
809 */
810 private void refreshTool(Object tool, Object dataObject)
811 {
812 RunData data = null;
813 PipelineData pipelineData = null;
814 if (dataObject instanceof PipelineData)
815 {
816 pipelineData = (PipelineData)dataObject;
817 data = pipelineData.getRunData();
818 if (tool instanceof PipelineDataApplicationTool)
819 {
820 ((PipelineDataApplicationTool) tool).refresh(pipelineData);
821 }
822 }
823 if (tool instanceof ApplicationTool)
824 {
825 ((ApplicationTool) tool).refresh();
826 }
827 else if (tool instanceof RunDataApplicationTool)
828 {
829 ((RunDataApplicationTool) tool).refresh(data);
830 }
831 }
832
833 private RunData getRunData(PipelineData pipelineData)
834 {
835 if (!(pipelineData instanceof RunData))
836 {
837 throw new RuntimeException("Can't cast to rundata from pipeline data.");
838 }
839 return (RunData)pipelineData;
840 }
841 }