/api/src/main/java/org/openmrs/api/context/Daemon.java

https://github.com/abhatia/openmrs · Java · 171 lines · 84 code · 28 blank · 59 comment · 8 complexity · 114947a6740cc7351f7c4ae3cebe9341 MD5 · raw file

  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. import org.openmrs.api.APIException;
  16. import org.openmrs.module.Module;
  17. import org.openmrs.module.ModuleException;
  18. import org.openmrs.module.ModuleFactory;
  19. import org.openmrs.scheduler.Task;
  20. import org.openmrs.scheduler.timer.TimerSchedulerTask;
  21. import sun.reflect.Reflection;
  22. /**
  23. * This class allows certain tasks to run with elevated privileges. Primary use is scheduling and
  24. * module startup when there is no user to authenticate as.
  25. */
  26. public class Daemon {
  27. /**
  28. * The uuid defined for the daemon user object
  29. */
  30. protected static final String DAEMON_USER_UUID = "A4F30A1B-5EB9-11DF-A648-37A07F9C90FB";
  31. private static final ThreadLocal<Boolean> isDaemonThread = new ThreadLocal<Boolean>();
  32. /**
  33. * This method should not be called directly. The {@link ModuleFactory#startModule(Module)}
  34. * method uses this to start the given module in a new thread that is authenticated as the
  35. * daemon user
  36. *
  37. * @param module the module to start
  38. * @returns the module returned from {@link ModuleFactory#startModuleInternal(Module)}
  39. */
  40. public static Module startModule(final Module module) throws ModuleException {
  41. // create a new thread and execute that task in it
  42. DaemonThread startModuleThread = new DaemonThread() {
  43. @Override
  44. public void run() {
  45. isDaemonThread.set(true);
  46. try {
  47. Context.openSession();
  48. returnedObject = ModuleFactory.startModuleInternal(module);
  49. }
  50. catch (Throwable t) {
  51. exceptionThrown = t;
  52. }
  53. finally {
  54. Context.closeSession();
  55. }
  56. }
  57. };
  58. startModuleThread.start();
  59. // wait for the "startModule" thread to finish
  60. try {
  61. startModuleThread.join();
  62. }
  63. catch (InterruptedException e) {
  64. // ignore
  65. }
  66. if (startModuleThread.exceptionThrown != null) {
  67. if (startModuleThread.exceptionThrown instanceof ModuleException)
  68. throw (ModuleException) startModuleThread.exceptionThrown;
  69. else
  70. throw new ModuleException("Unable to start module as Daemon", startModuleThread.exceptionThrown);
  71. }
  72. return (Module) startModuleThread.returnedObject;
  73. }
  74. /**
  75. * Executes the given task in a new thread that is authenticated as the daemon user. <br/>
  76. * <br/>
  77. * This can only be called from {@link TimerSchedulerTask} during actual task execution
  78. *
  79. * @param task the task to run
  80. * @should not be called from other methods other than TimerSchedulerTask
  81. */
  82. public static void executeScheduledTask(final Task task) throws Throwable {
  83. // quick check to make sure we're only being called by ourselves
  84. Class<?> callerClass = Reflection.getCallerClass(0);
  85. if (callerClass.isAssignableFrom(TimerSchedulerTask.class))
  86. throw new APIException("This method can only be called from the TimerSchedulerTask class, not "
  87. + callerClass.getName());
  88. // now create a new thread and execute that task in it
  89. DaemonThread executeTaskThread = new DaemonThread() {
  90. @Override
  91. public void run() {
  92. isDaemonThread.set(true);
  93. try {
  94. Context.openSession();
  95. task.execute();
  96. }
  97. catch (Throwable t) {
  98. exceptionThrown = t;
  99. }
  100. finally {
  101. Context.closeSession();
  102. }
  103. }
  104. };
  105. executeTaskThread.start();
  106. // wait for the "executeTaskThread" thread to finish
  107. try {
  108. executeTaskThread.join();
  109. }
  110. catch (InterruptedException e) {
  111. // ignore
  112. }
  113. if (executeTaskThread.exceptionThrown != null)
  114. throw executeTaskThread.exceptionThrown;
  115. }
  116. /**
  117. * @return true if the current thread was started by this class and so is a daemon thread that
  118. * has all privileges
  119. * @see Context#hasPrivilege(String)
  120. */
  121. public static boolean isDaemonThread() {
  122. Boolean b = isDaemonThread.get();
  123. if (b == null)
  124. return false;
  125. else
  126. return b.booleanValue();
  127. }
  128. /**
  129. * Thread class used by the {@link Daemon#startModule(Module)} and
  130. * {@link Daemon#executeScheduledTask(Task)} methods so that the returned object and the
  131. * exception thrown can be returned to calling class
  132. */
  133. private static class DaemonThread extends Thread {
  134. /**
  135. * The object returned from the method called in {@link #run()}
  136. */
  137. protected Object returnedObject = null;
  138. /**
  139. * The exception thrown (if any) by the method called in {@link #run()}
  140. */
  141. protected Throwable exceptionThrown = null;
  142. }
  143. }