Advertisement
Guest User

Untitled

a guest
Jan 30th, 2013
35
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 10.35 KB | None | 0 0
  1. /**
  2. * The contents of this file are subject to the OpenMRS Public License
  3. * Version 1.0 (the "License"); you may not use this file except in
  4. * compliance with the License. You may obtain a copy of the License at
  5. * http://license.openmrs.org
  6. *
  7. * Software distributed under the License is distributed on an "AS IS"
  8. * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
  9. * License for the specific language governing rights and limitations
  10. * under the License.
  11. *
  12. * Copyright (C) OpenMRS, LLC. All Rights Reserved.
  13. */
  14. package org.openmrs.api.context;
  15.  
  16. import org.openmrs.User;
  17. import org.openmrs.api.APIAuthenticationException;
  18. import org.openmrs.api.APIException;
  19. import org.openmrs.api.OpenmrsService;
  20. import org.openmrs.module.DaemonToken;
  21. import org.openmrs.module.Module;
  22. import org.openmrs.module.ModuleException;
  23. import org.openmrs.module.ModuleFactory;
  24. import org.openmrs.scheduler.Task;
  25. import org.openmrs.scheduler.timer.TimerSchedulerTask;
  26. import org.openmrs.util.OpenmrsSecurityManager;
  27. import org.springframework.context.support.AbstractRefreshableApplicationContext;
  28.  
  29. /**
  30. * This class allows certain tasks to run with elevated privileges. Primary use is scheduling and
  31. * module startup when there is no user to authenticate as.
  32. */
  33. public class Daemon {
  34.  
  35. /**
  36. * The uuid defined for the daemon user object
  37. */
  38. public static final String DAEMON_USER_UUID = "A4F30A1B-5EB9-11DF-A648-37A07F9C90FB";
  39.  
  40. protected static final ThreadLocal<Boolean> isDaemonThread = new ThreadLocal<Boolean>();
  41.  
  42. /**
  43. * @see #startModule(Module, boolean, AbstractRefreshableApplicationContext)
  44. */
  45. public static Module startModule(Module module) throws ModuleException {
  46. return startModule(module, false, null);
  47. }
  48.  
  49. /**
  50. * This method should not be called directly. The {@link ModuleFactory#startModule(Module)}
  51. * method uses this to start the given module in a new thread that is authenticated as the
  52. * daemon user. <br/>
  53. * If a non null application context is passed in, it gets refreshed to make the module's
  54. * services available
  55. *
  56. * @param module the module to start
  57. * @param isOpenmrsStartup Specifies whether this module is being started at application startup
  58. * or not
  59. * @param applicationContext the spring application context instance to refresh
  60. * @returns the module returned from {@link ModuleFactory#startModuleInternal(Module)}
  61. */
  62. public static Module startModule(final Module module, final boolean isOpenmrsStartup,
  63. final AbstractRefreshableApplicationContext applicationContext) throws ModuleException {
  64. // create a new thread and execute that task in it
  65. DaemonThread startModuleThread = new DaemonThread() {
  66.  
  67. @Override
  68. public void run() {
  69. isDaemonThread.set(true);
  70. try {
  71. Context.openSession();
  72. returnedObject = ModuleFactory.startModuleInternal(module, isOpenmrsStartup, applicationContext);
  73. }
  74. catch (Throwable t) {
  75. exceptionThrown = t;
  76. }
  77. finally {
  78. Context.closeSession();
  79. }
  80. }
  81. };
  82.  
  83. startModuleThread.start();
  84.  
  85. // wait for the "startModule" thread to finish
  86. try {
  87. startModuleThread.join();
  88. }
  89. catch (InterruptedException e) {
  90. // ignore
  91. }
  92.  
  93. if (startModuleThread.exceptionThrown != null) {
  94. if (startModuleThread.exceptionThrown instanceof ModuleException)
  95. throw (ModuleException) startModuleThread.exceptionThrown;
  96. else
  97. throw new ModuleException("Unable to start module as Daemon", startModuleThread.exceptionThrown);
  98. }
  99.  
  100. Module startedModule = (Module) startModuleThread.returnedObject;
  101.  
  102. return startedModule;
  103. }
  104.  
  105. /**
  106. * Executes the given task in a new thread that is authenticated as the daemon user. <br/>
  107. * <br/>
  108. * This can only be called from {@link TimerSchedulerTask} during actual task execution
  109. *
  110. * @param task the task to run
  111. * @should not be called from other methods other than TimerSchedulerTask
  112. * @should not throw error if called from a TimerSchedulerTask class
  113. */
  114. public static void executeScheduledTask(final Task task) throws Throwable {
  115.  
  116. // quick check to make sure we're only being called by ourselves
  117. //Class<?> callerClass = Reflection.getCallerClass(0);
  118. Class<?> callerClass = new OpenmrsSecurityManager().getCallerClass(0);
  119. if (!TimerSchedulerTask.class.isAssignableFrom(callerClass))
  120. throw new APIException("This method can only be called from the TimerSchedulerTask class, not "
  121. + callerClass.getName());
  122.  
  123. // now create a new thread and execute that task in it
  124. DaemonThread executeTaskThread = new DaemonThread() {
  125.  
  126. @Override
  127. public void run() {
  128. isDaemonThread.set(true);
  129.  
  130. try {
  131. Context.openSession();
  132. TimerSchedulerTask.execute(task);
  133. }
  134. catch (Throwable t) {
  135. exceptionThrown = t;
  136. }
  137. finally {
  138. Context.closeSession();
  139. }
  140.  
  141. }
  142. };
  143.  
  144. executeTaskThread.start();
  145.  
  146. // wait for the "executeTaskThread" thread to finish
  147. try {
  148. executeTaskThread.join();
  149. }
  150. catch (InterruptedException e) {
  151. // ignore
  152. }
  153.  
  154. if (executeTaskThread.exceptionThrown != null)
  155. throw executeTaskThread.exceptionThrown;
  156.  
  157. }
  158.  
  159. /**
  160. * Call this method if you are inside a Daemon thread (for example in a Module activator or a
  161. * scheduled task) and you want to start up a new parallel Daemon thread. You may only call this
  162. * method from a Daemon thread.
  163. *
  164. * @param runnable what to run in a new thread
  165. * @return the newly spawned {@link Thread}
  166. * @should throw error if called from a non daemon thread
  167. * @should not throw error if called from a daemon thread
  168. */
  169. public static Thread runInNewDaemonThread(final Runnable runnable) {
  170. // make sure we're already in a daemon thread
  171. if (!isDaemonThread())
  172. throw new APIAuthenticationException("Only daemon threads can spawn new daemon threads");
  173.  
  174. // we should consider making DaemonThread public, so the caller can access returnedObject and exceptionThrown
  175. DaemonThread thread = new DaemonThread() {
  176.  
  177. @Override
  178. public void run() {
  179. isDaemonThread.set(true);
  180. try {
  181. Context.openSession();
  182. runnable.run();
  183. }
  184. finally {
  185. Context.closeSession();
  186. }
  187. }
  188. };
  189.  
  190. thread.start();
  191. return thread;
  192. }
  193.  
  194. /**
  195. * @return true if the current thread was started by this class and so is a daemon thread that
  196. * has all privileges
  197. * @see Context#hasPrivilege(String)
  198. */
  199. public static boolean isDaemonThread() {
  200. Boolean b = isDaemonThread.get();
  201. if (b == null)
  202. return false;
  203. else
  204. return b.booleanValue();
  205. }
  206.  
  207. /**
  208. * Calls the {@link OpenmrsService#onStartup()} method, as a daemon, for an instance
  209. * implementing the {@link OpenmrsService} interface.
  210. *
  211. * @param openmrsService instance implementing the {@link OpenmrsService} interface.
  212. * @since 1.9
  213. */
  214. public static void runStartupForService(final OpenmrsService service) throws ModuleException {
  215.  
  216. DaemonThread onStartupThread = new DaemonThread() {
  217.  
  218. @Override
  219. public void run() {
  220. isDaemonThread.set(true);
  221. try {
  222. Context.openSession();
  223. service.onStartup();
  224. }
  225. catch (Throwable t) {
  226. exceptionThrown = t;
  227. }
  228. finally {
  229. Context.closeSession();
  230. }
  231. }
  232. };
  233.  
  234. onStartupThread.start();
  235.  
  236. // wait for the "onStartup" thread to finish
  237. try {
  238. onStartupThread.join();
  239. }
  240. catch (InterruptedException e) {
  241. // ignore
  242. e.printStackTrace();
  243. }
  244.  
  245. if (onStartupThread.exceptionThrown != null) {
  246. if (onStartupThread.exceptionThrown instanceof ModuleException)
  247. throw (ModuleException) onStartupThread.exceptionThrown;
  248. else
  249. throw new ModuleException("Unable to run onStartup() method as Daemon", onStartupThread.exceptionThrown);
  250. }
  251. }
  252.  
  253. /**
  254. * Executes the given runnable in a new thread that is authenticated as the daemon user.
  255. *
  256. * @param runnable an object implementing the {@link Runnable} interface.
  257. * @param token the token required to run code as the daemon user
  258. * @return the newly spawned {@link Thread}
  259. * @since 1.9.2
  260. */
  261. public static Thread runInDaemonThread(final Runnable runnable, DaemonToken token) {
  262. if (!ModuleFactory.isTokenValid(token)) {
  263. throw new ContextAuthenticationException("Invalid token " + token);
  264. }
  265.  
  266. DaemonThread thread = new DaemonThread() {
  267.  
  268. @Override
  269. public void run() {
  270. isDaemonThread.set(true);
  271. try {
  272. Context.openSession();
  273. runnable.run();
  274. }
  275. finally {
  276. Context.closeSession();
  277. }
  278. }
  279. };
  280.  
  281. thread.start();
  282. return thread;
  283. }
  284.  
  285. /**
  286. * Executes the given runnable in a new thread that is authenticated as the daemon user and wait
  287. * for the thread to finish.
  288. *
  289. * @param runnable an object implementing the {@link Runnable} interface.
  290. * @param token the token required to run code as the daemon user
  291. * @return the newly spawned {@link Thread}
  292. * @since 1.9.2
  293. */
  294. public static void runInDaemonThreadAndWait(final Runnable runnable, DaemonToken token) {
  295. Thread daemonThread = runInDaemonThread(runnable, token);
  296.  
  297. try {
  298. daemonThread.join();
  299. }
  300. catch (InterruptedException e) {
  301. //Ignore
  302. }
  303. }
  304.  
  305. /**
  306. * Thread class used by the {@link Daemon#startModule(Module)} and
  307. * {@link Daemon#executeScheduledTask(Task)} methods so that the returned object and the
  308. * exception thrown can be returned to calling class
  309. */
  310. protected static class DaemonThread extends Thread {
  311.  
  312. /**
  313. * The object returned from the method called in {@link #run()}
  314. */
  315. protected Object returnedObject = null;
  316.  
  317. /**
  318. * The exception thrown (if any) by the method called in {@link #run()}
  319. */
  320. protected Throwable exceptionThrown = null;
  321.  
  322. /**
  323. * Gets the exception thrown (if any) by the method called in {@link #run()}
  324. *
  325. * @return the thrown exception (if any).
  326. */
  327. public Throwable getExceptionThrown() {
  328. return exceptionThrown;
  329. }
  330. }
  331.  
  332. /**
  333. * @param daemon,
  334. * however this is not the preferred method for checking to see if the current thread is a daemon thread
  335. * rather use Daemon.isDeamonThread()
  336. * the isDaemonThread is preferred for checking to see if you are in that thread or if the current thread is daemon.
  337. */
  338. public boolean isDaemonUser(User daemon) {
  339. return daemon != null;
  340. }
  341. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement