PageRenderTime 42ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/java/src/org/jpublish/action/ActionManager.java

http://jpublish.googlecode.com/
Java | 747 lines | 384 code | 110 blank | 253 comment | 68 complexity | afdcf16601d1fe789052f87845b80a76 MD5 | raw file
Possible License(s): Apache-2.0, BSD-3-Clause
  1. /*
  2. * Copyright 2004-2007 the original author or authors.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. *
  16. */
  17. package org.jpublish.action;
  18. import com.anthonyeden.lib.config.Configuration;
  19. import com.anthonyeden.lib.config.ConfigurationException;
  20. import com.anthonyeden.lib.util.ClassUtilities;
  21. import com.anthonyeden.lib.util.IOUtilities;
  22. import com.atlassian.util.profiling.UtilTimerStack;
  23. import com.atlassian.util.profiling.object.ObjectProfiler;
  24. import org.apache.commons.logging.Log;
  25. import org.apache.commons.logging.LogFactory;
  26. import org.jpublish.JPublishContext;
  27. import org.jpublish.JPublishModule;
  28. import org.jpublish.SiteContext;
  29. import org.jpublish.util.*;
  30. import org.jpublish.util.vfs.VFSFile;
  31. import org.jpublish.util.vfs.VFSProvider;
  32. import org.jpublish.util.vfs.provider.filesystem.FileSystemProvider;
  33. import java.io.*;
  34. import java.lang.reflect.Method;
  35. import java.util.*;
  36. /**
  37. * Class which manages all actions in the JPublish framework.
  38. *
  39. * @author Anthony Eden
  40. * @author <a href="mailto:florin.patrascu@gmail.com">Florin T.PATRASCU</a>
  41. */
  42. public class ActionManager {
  43. private static final String ATTRIBUTE_NAME = "name";
  44. private static final String ATTRIBUTE_PATH = "path";
  45. private static Log log = LogFactory.getLog(ActionManager.class);
  46. private Map definedActions;
  47. private List classPathElements;
  48. private List startupActions;
  49. private List shutdownActions;
  50. private List globalActions;
  51. private List pathActions;
  52. private List preEvaluationActions;
  53. private List postEvaluationActions;
  54. private SiteContext siteContext;
  55. private VFSProvider provider;
  56. private JPublishContext startupContext;
  57. private Map cachedScriptActions = new HashMap();
  58. public static final String SCRIPT_ACTION = "ScriptAction";
  59. public static final String EXECUTE_METHOD_NAME = "execute";
  60. public static final String PATH_ACTION = "PathAction";
  61. /**
  62. * Construct a new ActionManager with the given SiteContext.
  63. *
  64. * @param siteContext The SiteContext
  65. */
  66. public ActionManager(SiteContext siteContext) {
  67. this.siteContext = siteContext;
  68. this.definedActions = new HashMap();
  69. this.startupActions = new ArrayList();
  70. this.shutdownActions = new ArrayList();
  71. this.globalActions = new ArrayList();
  72. this.pathActions = new ArrayList();
  73. this.preEvaluationActions = new ArrayList();
  74. this.postEvaluationActions = new ArrayList();
  75. this.classPathElements = new ArrayList();
  76. this.startupContext = new JPublishContext(null);
  77. // add the DateUtilities to the context
  78. this.startupContext.put(JPublishContext.JPUBLISH_DATE_UTILITIES, DateUtilities.getInstance());
  79. // add the NumberUtilities to the context
  80. this.startupContext.put(JPublishContext.JPUBLISH_NUMBER_UTILITIES, NumberUtilities.getInstance());
  81. // add the messages log to the context
  82. this.startupContext.put(JPublishContext.JPUBLISH_SYSLOG, SiteContext.syslog);
  83. // expose the SiteContext
  84. this.startupContext.put(JPublishContext.JPUBLISH_SITE, siteContext);
  85. //todo: What else do we need in the startupContext?
  86. }
  87. /**
  88. * Get a Map of all defined actions.
  89. *
  90. * @return A Map of defined actions
  91. */
  92. public Map getDefinedActions() {
  93. return definedActions;
  94. }
  95. /**
  96. * Add an action.
  97. *
  98. * @param name The action name
  99. * @param action The Action implementation
  100. */
  101. public void addAction(String name, Action action) {
  102. definedActions.put(name, action);
  103. }
  104. /**
  105. * Remove an action.
  106. *
  107. * @param name The action name
  108. */
  109. public void removeAction(String name) {
  110. definedActions.remove(name);
  111. }
  112. /**
  113. * Get the class path used by script actions for loading classes.
  114. *
  115. * @return The class path
  116. */
  117. public synchronized String getClassPath() {
  118. StringBuffer buffer = new StringBuffer();
  119. Iterator classPathElements = getClassPathElements().iterator();
  120. while (classPathElements.hasNext()) {
  121. buffer.append(classPathElements.next().toString());
  122. if (classPathElements.hasNext()) {
  123. buffer.append(System.getProperty("path.separator"));
  124. }
  125. }
  126. return buffer.toString();
  127. }
  128. /**
  129. * Get a list of all elements in the ActionManager class path.
  130. *
  131. * @return The class path elements list
  132. */
  133. public List getClassPathElements() {
  134. return classPathElements;
  135. }
  136. /**
  137. * Get a List of startup actions.
  138. *
  139. * @return List of startup actions
  140. */
  141. public List getStartupActions() {
  142. return startupActions;
  143. }
  144. /**
  145. * Get a List of shutdown actions.
  146. *
  147. * @return List of shutdown actions
  148. */
  149. public List getShutdownActions() {
  150. return shutdownActions;
  151. }
  152. /**
  153. * Get a List of global actions.
  154. *
  155. * @return List of global actions
  156. */
  157. public List getGlobalActions() {
  158. return globalActions;
  159. }
  160. /**
  161. * Get the List of path actions.
  162. *
  163. * @return The path actions
  164. */
  165. public List getPathActions() {
  166. return pathActions;
  167. }
  168. /**
  169. * Get the List of actions which are executed immediately
  170. * upon receipt of any request. These actions are
  171. * executed before a page search occurs.
  172. *
  173. * @return The pre-evaluation actions
  174. * @since 1.3
  175. */
  176. public List getPreEvaluationActions() {
  177. return preEvaluationActions;
  178. }
  179. /**
  180. * Get the List of actions which are executed after the
  181. * HTTP request has been completed, but before the response
  182. * is sent back to the client.
  183. *
  184. * @return The post-evaluation actions
  185. * @since 1.3
  186. */
  187. public List getPostEvaluationActions() {
  188. return postEvaluationActions;
  189. }
  190. /**
  191. * Execute all startup actions.
  192. *
  193. * @throws Exception Any Exception
  194. */
  195. public void executeStartupActions() throws Exception {
  196. if (log.isDebugEnabled())
  197. log.debug("Executing startup actions");
  198. Iterator actions = getStartupActions().iterator();
  199. while (actions.hasNext()) {
  200. ActionWrapper action = (ActionWrapper) actions.next();
  201. action.execute(this.startupContext);
  202. }
  203. }
  204. /**
  205. * Execute all shutdown actions.
  206. *
  207. * @throws Exception Any Exception
  208. */
  209. public void executeShutdownActions() throws Exception {
  210. if (log.isDebugEnabled())
  211. log.debug("Executing shutdown actions");
  212. Iterator actions = getShutdownActions().iterator();
  213. while (actions.hasNext()) {
  214. ActionWrapper action = (ActionWrapper) actions.next();
  215. action.execute(this.startupContext);
  216. }
  217. }
  218. /**
  219. * Execute all global actions using the given context.
  220. *
  221. * @param context The current context
  222. * @return Redirection URL or null
  223. * @throws Exception
  224. */
  225. public String executeGlobalActions(JPublishContext context) throws Exception {
  226. List globalActions = getGlobalActions();
  227. if (globalActions == null) {
  228. log.error("Global actions list is null");
  229. throw new NullPointerException("Global actions is null");
  230. }
  231. Iterator actions = globalActions.iterator();
  232. while (actions.hasNext()) {
  233. ActionWrapper action = (ActionWrapper) actions.next();
  234. if (action != null) {
  235. action.execute(context);
  236. String redirect = (String) context.get("redirect");
  237. if (redirect != null) {
  238. return redirect;
  239. }
  240. } else {
  241. log.error("Action retrieved from iterator is null");
  242. }
  243. }
  244. return null;
  245. }
  246. /**
  247. * Execute the path actions with the given context. If any of the
  248. * actions sets the value redirect in the context then that signals that
  249. * the servlet should redirect the request to the specified URL.
  250. *
  251. * @param path The request path
  252. * @param context The current context
  253. * @return The redirect value or null
  254. * @throws Exception
  255. */
  256. public String executePathActions(String path, JPublishContext context) throws Exception {
  257. Iterator actions = getPathActions().iterator();
  258. while (actions.hasNext()) {
  259. ActionWrapper actionWrapper = (ActionWrapper) actions.next();
  260. PathAction action = (PathAction) actionWrapper.getAction();
  261. if (PathUtilities.match(path, action.getPath())) {
  262. actionWrapper.execute(context);
  263. String redirect = (String) context.get("redirect");
  264. if (redirect != null) {
  265. return redirect;
  266. }
  267. }
  268. }
  269. return null;
  270. }
  271. /**
  272. * Execute pre-evaluation actions. Pre-evaluation actions are only
  273. * executed if their path argument matches the current path.
  274. * <p/>
  275. * <p><b>Note:</b> Since these actions are executed prior to
  276. * locating the page the page variable is not in the context.
  277. * <p/>
  278. * <p>To stop processing and return immediately, set the value
  279. * <code>stop-processing</code> in the context to a non-null value.
  280. *
  281. * @param path The request path
  282. * @param context The current request context
  283. * @return True if the processing should stop
  284. * @throws Exception
  285. * @since 1.3
  286. */
  287. public boolean executePreEvaluationActions(String path, JPublishContext context) throws Exception {
  288. Iterator actions = getPreEvaluationActions().iterator();
  289. while (actions.hasNext()) {
  290. ActionWrapper actionWrapper = (ActionWrapper) actions.next();
  291. PathAction action = (PathAction) actionWrapper.getAction();
  292. if (PathUtilities.match(path, action.getPath())) {
  293. actionWrapper.execute(context);
  294. String stopProcessingFlag = (String) context.get("stop-processing");
  295. if (stopProcessingFlag != null) {
  296. return true;
  297. }
  298. }
  299. }
  300. return false;
  301. }
  302. /**
  303. * Execute post-evaluation actions.
  304. *
  305. * @param path The request path
  306. * @param context The request context
  307. * @throws Exception
  308. * @since 1.3
  309. */
  310. public void executePostEvaluationActions(String path, JPublishContext context) throws Exception {
  311. Iterator actions = getPostEvaluationActions().iterator();
  312. while (actions.hasNext()) {
  313. ActionWrapper actionWrapper = (ActionWrapper) actions.next();
  314. PathAction action = (PathAction) actionWrapper.getAction();
  315. if (PathUtilities.match(path, action.getPath())) {
  316. actionWrapper.execute(context);
  317. //String redirect = (String)context.get("redirect");
  318. //if(redirect != null){
  319. // return redirect;
  320. //}
  321. }
  322. }
  323. }
  324. /**
  325. * Execute the named action with the given context. If the action sets
  326. * the value redirect in the context then that signals that the servlet
  327. * should redirect the request to the specified URL.
  328. *
  329. * @param name The action name
  330. * @param context The current context
  331. * @return The redirect value or null
  332. * @throws Exception
  333. */
  334. public String execute(String name, JPublishContext context) throws Exception {
  335. return execute(name, context, null);
  336. }
  337. /**
  338. * Execute the named action with the given context. If the action sets
  339. * the value redirect in the context then that signals that the servlet
  340. * should redirect the request to the specified URL.
  341. *
  342. * @param name The action name
  343. * @param context The current context
  344. * @param configuration The Configuration object
  345. * @return The redirect value or null
  346. * @throws Exception
  347. */
  348. public String execute(String name, JPublishContext context,
  349. Configuration configuration) throws Exception {
  350. if (log.isDebugEnabled())
  351. log.debug("Executing action: " + name);
  352. Action action = findAction(name);
  353. final String className = action.getClass().getName();
  354. boolean scriptActionWrapper = action.getClass().getName().indexOf(SCRIPT_ACTION) >= 0;
  355. if (SiteContext.getProfiling() && !scriptActionWrapper) {
  356. try {
  357. UtilTimerStack.push(className);
  358. Class[] paramTypes = {JPublishContext.class, Configuration.class};
  359. Object[] params = {context, configuration};
  360. Method execute = action.getClass().getMethod(EXECUTE_METHOD_NAME, paramTypes);
  361. ObjectProfiler.profiledInvoke(execute, action, params);
  362. } finally {
  363. UtilTimerStack.pop(className);
  364. }
  365. } else {
  366. action.execute(context, configuration);
  367. }
  368. String redirect = (String) context.get("redirect");
  369. if (redirect != null) {
  370. return redirect;
  371. }
  372. return null;
  373. }
  374. /**
  375. * Return the text for the specified script action.
  376. *
  377. * @param name The script name
  378. * @return The script action text
  379. * @throws IOException
  380. */
  381. public String getScriptActionText(String name) throws IOException {
  382. File actionRoot = siteContext.getRealActionRoot();
  383. File actionFile = new File(actionRoot, name);
  384. return FileCopyUtils.copyToString(new FileReader(actionFile));
  385. }
  386. /**
  387. * Set the text for the specified script action.
  388. *
  389. * @param name The script action name
  390. * @param scriptActionText The script action text
  391. * @throws IOException
  392. */
  393. public void setScriptActionText(String name, String scriptActionText)
  394. throws IOException {
  395. File actionRoot = siteContext.getRealActionRoot();
  396. File actionFile = new File(actionRoot, name);
  397. PrintWriter writer = null;
  398. try {
  399. writer = new PrintWriter(new FileWriter(actionFile));
  400. writer.print(scriptActionText);
  401. } finally {
  402. IOUtilities.close(writer);
  403. }
  404. }
  405. /**
  406. * Remove the named script action.
  407. *
  408. * @param name The name
  409. */
  410. public void removeScriptAction(String name) {
  411. File actionRoot = siteContext.getRealActionRoot();
  412. File actionFile = new File(actionRoot, name);
  413. actionFile.delete();
  414. }
  415. /**
  416. * Make the directory for the specified path. Parent directories
  417. * will also be created if they do not exist.
  418. *
  419. * @param path The directory path
  420. */
  421. public void makeDirectory(String path) {
  422. File file = new File(siteContext.getRealActionRoot(), path);
  423. file.mkdirs();
  424. }
  425. /**
  426. * Remove the directory for the specified path. The directory
  427. * must be empty.
  428. *
  429. * @param path The path
  430. * @throws Exception
  431. */
  432. public void removeDirectory(String path) throws Exception {
  433. if (log.isInfoEnabled())
  434. log.info("Remove directory: " + path);
  435. File file = new File(siteContext.getRealActionRoot(), path);
  436. if (log.isDebugEnabled())
  437. log.debug("Deleting file: " + file.getAbsolutePath());
  438. if (file.isDirectory()) {
  439. file.delete();
  440. } else {
  441. throw new Exception("Path is not a directory: " + path);
  442. }
  443. }
  444. /**
  445. * Get the Virtual File System root file. The Virtual File System
  446. * provides a datasource-independent way of navigating through all
  447. * items known to the StaticResourceManager.
  448. *
  449. * @return The root VFSFile
  450. * @throws Exception
  451. */
  452. public VFSFile getVFSRoot() throws Exception {
  453. if (provider == null) {
  454. provider = new FileSystemProvider(siteContext.getRealActionRoot());
  455. }
  456. return provider.getRoot();
  457. }
  458. /**
  459. * Find an action with the given name. The name may be the name of an
  460. * action registered with the ActionManager at startup, an action from a
  461. * module, a partial file path rooted in the action root directory or a
  462. * fully qualified Java class.
  463. *
  464. * @param name The name of the action
  465. * @return The action
  466. * @throws ActionNotFoundException If the action is not found
  467. */
  468. public Action findAction(String name) {
  469. // look in registered classes first
  470. if (log.isDebugEnabled())
  471. log.debug("Looking for registered action: " + name);
  472. Action action = (Action) definedActions.get(name);
  473. if (action != null) {
  474. return action;
  475. }
  476. // look in modules
  477. if (log.isDebugEnabled())
  478. log.debug("Looking for action in modules.");
  479. Iterator modules = siteContext.getModules().iterator();
  480. while (modules.hasNext()) {
  481. JPublishModule module = (JPublishModule) modules.next();
  482. if (module.getDefinedActions() != null && !module.getDefinedActions().isEmpty()) {
  483. action = (Action) (module.getDefinedActions().get(name));
  484. if (action != null) {
  485. return action;
  486. }
  487. }
  488. }
  489. // look in the action directory for scripts
  490. action = (Action) cachedScriptActions.get(name);
  491. if (action != null) {
  492. if (log.isDebugEnabled())
  493. log.debug("Action: " + name + ", found in script actions cache.");
  494. return action;
  495. }
  496. if (log.isDebugEnabled())
  497. log.debug("Looking for action in action root.");
  498. File actionRoot = siteContext.getRealActionRoot();
  499. if (log.isDebugEnabled())
  500. log.debug("Action root: " + actionRoot);
  501. File actionFile = new File(actionRoot, name);
  502. if (actionFile.exists()) {
  503. if (log.isDebugEnabled())
  504. log.debug("Action found [" + actionFile + "]");
  505. action = new ScriptAction(siteContext, actionFile);
  506. cachedScriptActions.put(name, action);
  507. return action;
  508. }
  509. // look in classpath
  510. try {
  511. if (log.isDebugEnabled())
  512. log.debug("Looking for action in the classpath.");
  513. action = (Action) ClassUtilities.loadClass(name).newInstance();
  514. return action;
  515. } catch (Exception e) {
  516. e.printStackTrace();
  517. throw new ActionNotFoundException(e, name);
  518. }
  519. }
  520. /**
  521. * Load the ActionManager's configuration from the given configuration
  522. * object.
  523. *
  524. * @param configuration The configuration object
  525. * @throws ConfigurationException
  526. */
  527. public void loadConfiguration(Configuration configuration) throws ConfigurationException {
  528. try {
  529. // load action definitions
  530. Iterator defineActionElements = configuration.getChildren("define-action").iterator();
  531. while (defineActionElements.hasNext()) {
  532. Configuration defineActionElement = (Configuration) defineActionElements.next();
  533. String name = defineActionElement.getAttribute(ATTRIBUTE_NAME);
  534. String className = defineActionElement.getAttribute("classname");
  535. Action action = (Action) ClassUtilities.loadClass(className).newInstance();
  536. if (log.isDebugEnabled())
  537. log.debug("Defined action: " + name + " [" + className + "]");
  538. definedActions.put(name, action);
  539. }
  540. // load startup actions
  541. Iterator startupActionElements = configuration.getChildren("startup-action").iterator();
  542. while (startupActionElements.hasNext()) {
  543. Configuration startupActionElement =
  544. (Configuration) startupActionElements.next();
  545. String name = startupActionElement.getAttribute(ATTRIBUTE_NAME);
  546. startupActions.add(new ActionWrapper(findAction(name),
  547. startupActionElement));
  548. }
  549. // load shutdown actions
  550. Iterator shutdownActionElements = configuration.getChildren("shutdown-action").iterator();
  551. while (shutdownActionElements.hasNext()) {
  552. Configuration shutdownActionElement =
  553. (Configuration) shutdownActionElements.next();
  554. String name = shutdownActionElement.getAttribute(ATTRIBUTE_NAME);
  555. shutdownActions.add(new ActionWrapper(findAction(name),
  556. shutdownActionElement));
  557. }
  558. // load global actions
  559. if (log.isDebugEnabled())
  560. log.debug("Configuring global actions");
  561. Iterator globalActionElements = configuration.getChildren("global-action").iterator();
  562. while (globalActionElements.hasNext()) {
  563. Configuration globalActionElement =
  564. (Configuration) globalActionElements.next();
  565. String name = globalActionElement.getAttribute(ATTRIBUTE_NAME);
  566. if (log.isDebugEnabled())
  567. log.debug("Finding global action '" + name + "'");
  568. Action action = findAction(name);
  569. if (action == null) {
  570. log.error("No action '" + name + "' found");
  571. } else {
  572. if (log.isDebugEnabled())
  573. log.debug("Action '" + name + "' found");
  574. globalActions.add(new ActionWrapper(action,
  575. globalActionElement));
  576. }
  577. }
  578. // load path actions
  579. Iterator pathActionElements = configuration.getChildren("path-action").iterator();
  580. while (pathActionElements.hasNext()) {
  581. Configuration pathActionElement =
  582. (Configuration) pathActionElements.next();
  583. String name = pathActionElement.getAttribute(ATTRIBUTE_NAME);
  584. String path = pathActionElement.getAttribute(ATTRIBUTE_PATH);
  585. pathActions.add(new ActionWrapper(
  586. new PathAction(path, findAction(name)), pathActionElement));
  587. }
  588. // load pre-evaluation actions
  589. Iterator preEvaluationActionElements = configuration.getChildren("pre-evaluation-action").iterator();
  590. while (preEvaluationActionElements.hasNext()) {
  591. Configuration preEvaluationActionElement = (Configuration) preEvaluationActionElements.next();
  592. String name = preEvaluationActionElement.getAttribute(ATTRIBUTE_NAME);
  593. String path = preEvaluationActionElement.getAttribute(ATTRIBUTE_PATH);
  594. preEvaluationActions.add(new ActionWrapper(
  595. new PathAction(path, findAction(name)),
  596. preEvaluationActionElement));
  597. }
  598. // load post-evaluation actions
  599. Iterator postEvaluationActionElements = configuration.getChildren("post-evaluation-action").iterator();
  600. while (postEvaluationActionElements.hasNext()) {
  601. Configuration postEvaluationActionElement =
  602. (Configuration) postEvaluationActionElements.next();
  603. String name = postEvaluationActionElement.getAttribute(ATTRIBUTE_NAME);
  604. String path = postEvaluationActionElement.getAttribute(ATTRIBUTE_PATH);
  605. postEvaluationActions.add(new ActionWrapper(
  606. new PathAction(path, findAction(name)),
  607. postEvaluationActionElement));
  608. }
  609. } catch (Exception e) {
  610. throw new ConfigurationException(e);
  611. }
  612. }
  613. /**
  614. * Get an Iterator of paths of action scripts which are known to the
  615. * ActionManager.
  616. *
  617. * @return An iterator of paths
  618. * @throws Exception
  619. */
  620. public Iterator getPaths() throws Exception {
  621. return getPaths("");
  622. }
  623. /**
  624. * Get an Iterator of paths of action scripts which are known to the
  625. * ActionManager, starting from the specified base path.
  626. *
  627. * @param base The base path
  628. * @return An iterator of paths
  629. * @throws Exception
  630. */
  631. public Iterator getPaths(String base) throws Exception {
  632. File actionRoot = siteContext.getRealActionRoot();
  633. File baseFile = new File(actionRoot, base);
  634. return new FileToPathIterator(baseFile.toString(),
  635. new BreadthFirstFileTreeIterator(baseFile));
  636. }
  637. }