Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /**
- * The contents of this file are subject to the OpenMRS Public License
- * Version 1.0 (the "License"); you may not use this file except in
- * compliance with the License. You may obtain a copy of the License at
- * http://license.openmrs.org
- *
- * Software distributed under the License is distributed on an "AS IS"
- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
- * License for the specific language governing rights and limitations
- * under the License.
- *
- * Copyright (C) OpenMRS, LLC. All Rights Reserved.
- */
- package org.openmrs.api.context;
- import org.openmrs.User;
- import org.openmrs.api.APIAuthenticationException;
- import org.openmrs.api.APIException;
- import org.openmrs.api.OpenmrsService;
- import org.openmrs.module.DaemonToken;
- import org.openmrs.module.Module;
- import org.openmrs.module.ModuleException;
- import org.openmrs.module.ModuleFactory;
- import org.openmrs.scheduler.Task;
- import org.openmrs.scheduler.timer.TimerSchedulerTask;
- import org.openmrs.util.OpenmrsSecurityManager;
- import org.springframework.context.support.AbstractRefreshableApplicationContext;
- /**
- * This class allows certain tasks to run with elevated privileges. Primary use is scheduling and
- * module startup when there is no user to authenticate as.
- */
- public class Daemon {
- /**
- * The uuid defined for the daemon user object
- */
- public static final String DAEMON_USER_UUID = "A4F30A1B-5EB9-11DF-A648-37A07F9C90FB";
- protected static final ThreadLocal<Boolean> isDaemonThread = new ThreadLocal<Boolean>();
- /**
- * @see #startModule(Module, boolean, AbstractRefreshableApplicationContext)
- */
- public static Module startModule(Module module) throws ModuleException {
- return startModule(module, false, null);
- }
- /**
- * This method should not be called directly. The {@link ModuleFactory#startModule(Module)}
- * method uses this to start the given module in a new thread that is authenticated as the
- * daemon user. <br/>
- * If a non null application context is passed in, it gets refreshed to make the module's
- * services available
- *
- * @param module the module to start
- * @param isOpenmrsStartup Specifies whether this module is being started at application startup
- * or not
- * @param applicationContext the spring application context instance to refresh
- * @returns the module returned from {@link ModuleFactory#startModuleInternal(Module)}
- */
- public static Module startModule(final Module module, final boolean isOpenmrsStartup,
- final AbstractRefreshableApplicationContext applicationContext) throws ModuleException {
- // create a new thread and execute that task in it
- DaemonThread startModuleThread = new DaemonThread() {
- @Override
- public void run() {
- isDaemonThread.set(true);
- try {
- Context.openSession();
- returnedObject = ModuleFactory.startModuleInternal(module, isOpenmrsStartup, applicationContext);
- }
- catch (Throwable t) {
- exceptionThrown = t;
- }
- finally {
- Context.closeSession();
- }
- }
- };
- startModuleThread.start();
- // wait for the "startModule" thread to finish
- try {
- startModuleThread.join();
- }
- catch (InterruptedException e) {
- // ignore
- }
- if (startModuleThread.exceptionThrown != null) {
- if (startModuleThread.exceptionThrown instanceof ModuleException)
- throw (ModuleException) startModuleThread.exceptionThrown;
- else
- throw new ModuleException("Unable to start module as Daemon", startModuleThread.exceptionThrown);
- }
- Module startedModule = (Module) startModuleThread.returnedObject;
- return startedModule;
- }
- /**
- * Executes the given task in a new thread that is authenticated as the daemon user. <br/>
- * <br/>
- * This can only be called from {@link TimerSchedulerTask} during actual task execution
- *
- * @param task the task to run
- * @should not be called from other methods other than TimerSchedulerTask
- * @should not throw error if called from a TimerSchedulerTask class
- */
- public static void executeScheduledTask(final Task task) throws Throwable {
- // quick check to make sure we're only being called by ourselves
- //Class<?> callerClass = Reflection.getCallerClass(0);
- Class<?> callerClass = new OpenmrsSecurityManager().getCallerClass(0);
- if (!TimerSchedulerTask.class.isAssignableFrom(callerClass))
- throw new APIException("This method can only be called from the TimerSchedulerTask class, not "
- + callerClass.getName());
- // now create a new thread and execute that task in it
- DaemonThread executeTaskThread = new DaemonThread() {
- @Override
- public void run() {
- isDaemonThread.set(true);
- try {
- Context.openSession();
- TimerSchedulerTask.execute(task);
- }
- catch (Throwable t) {
- exceptionThrown = t;
- }
- finally {
- Context.closeSession();
- }
- }
- };
- executeTaskThread.start();
- // wait for the "executeTaskThread" thread to finish
- try {
- executeTaskThread.join();
- }
- catch (InterruptedException e) {
- // ignore
- }
- if (executeTaskThread.exceptionThrown != null)
- throw executeTaskThread.exceptionThrown;
- }
- /**
- * Call this method if you are inside a Daemon thread (for example in a Module activator or a
- * scheduled task) and you want to start up a new parallel Daemon thread. You may only call this
- * method from a Daemon thread.
- *
- * @param runnable what to run in a new thread
- * @return the newly spawned {@link Thread}
- * @should throw error if called from a non daemon thread
- * @should not throw error if called from a daemon thread
- */
- public static Thread runInNewDaemonThread(final Runnable runnable) {
- // make sure we're already in a daemon thread
- if (!isDaemonThread())
- throw new APIAuthenticationException("Only daemon threads can spawn new daemon threads");
- // we should consider making DaemonThread public, so the caller can access returnedObject and exceptionThrown
- DaemonThread thread = new DaemonThread() {
- @Override
- public void run() {
- isDaemonThread.set(true);
- try {
- Context.openSession();
- runnable.run();
- }
- finally {
- Context.closeSession();
- }
- }
- };
- thread.start();
- return thread;
- }
- /**
- * @return true if the current thread was started by this class and so is a daemon thread that
- * has all privileges
- * @see Context#hasPrivilege(String)
- */
- public static boolean isDaemonThread() {
- Boolean b = isDaemonThread.get();
- if (b == null)
- return false;
- else
- return b.booleanValue();
- }
- /**
- * Calls the {@link OpenmrsService#onStartup()} method, as a daemon, for an instance
- * implementing the {@link OpenmrsService} interface.
- *
- * @param openmrsService instance implementing the {@link OpenmrsService} interface.
- * @since 1.9
- */
- public static void runStartupForService(final OpenmrsService service) throws ModuleException {
- DaemonThread onStartupThread = new DaemonThread() {
- @Override
- public void run() {
- isDaemonThread.set(true);
- try {
- Context.openSession();
- service.onStartup();
- }
- catch (Throwable t) {
- exceptionThrown = t;
- }
- finally {
- Context.closeSession();
- }
- }
- };
- onStartupThread.start();
- // wait for the "onStartup" thread to finish
- try {
- onStartupThread.join();
- }
- catch (InterruptedException e) {
- // ignore
- e.printStackTrace();
- }
- if (onStartupThread.exceptionThrown != null) {
- if (onStartupThread.exceptionThrown instanceof ModuleException)
- throw (ModuleException) onStartupThread.exceptionThrown;
- else
- throw new ModuleException("Unable to run onStartup() method as Daemon", onStartupThread.exceptionThrown);
- }
- }
- /**
- * Executes the given runnable in a new thread that is authenticated as the daemon user.
- *
- * @param runnable an object implementing the {@link Runnable} interface.
- * @param token the token required to run code as the daemon user
- * @return the newly spawned {@link Thread}
- * @since 1.9.2
- */
- public static Thread runInDaemonThread(final Runnable runnable, DaemonToken token) {
- if (!ModuleFactory.isTokenValid(token)) {
- throw new ContextAuthenticationException("Invalid token " + token);
- }
- DaemonThread thread = new DaemonThread() {
- @Override
- public void run() {
- isDaemonThread.set(true);
- try {
- Context.openSession();
- runnable.run();
- }
- finally {
- Context.closeSession();
- }
- }
- };
- thread.start();
- return thread;
- }
- /**
- * Executes the given runnable in a new thread that is authenticated as the daemon user and wait
- * for the thread to finish.
- *
- * @param runnable an object implementing the {@link Runnable} interface.
- * @param token the token required to run code as the daemon user
- * @return the newly spawned {@link Thread}
- * @since 1.9.2
- */
- public static void runInDaemonThreadAndWait(final Runnable runnable, DaemonToken token) {
- Thread daemonThread = runInDaemonThread(runnable, token);
- try {
- daemonThread.join();
- }
- catch (InterruptedException e) {
- //Ignore
- }
- }
- /**
- * Thread class used by the {@link Daemon#startModule(Module)} and
- * {@link Daemon#executeScheduledTask(Task)} methods so that the returned object and the
- * exception thrown can be returned to calling class
- */
- protected static class DaemonThread extends Thread {
- /**
- * The object returned from the method called in {@link #run()}
- */
- protected Object returnedObject = null;
- /**
- * The exception thrown (if any) by the method called in {@link #run()}
- */
- protected Throwable exceptionThrown = null;
- /**
- * Gets the exception thrown (if any) by the method called in {@link #run()}
- *
- * @return the thrown exception (if any).
- */
- public Throwable getExceptionThrown() {
- return exceptionThrown;
- }
- }
- /**
- * @param daemon,
- * however this is not the preferred method for checking to see if the current thread is a daemon thread
- * rather use Daemon.isDeamonThread()
- * the isDaemonThread is preferred for checking to see if you are in that thread or if the current thread is daemon.
- */
- public boolean isDaemonUser(User daemon) {
- return daemon != null;
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement