/**
* This Source Code Form is subject to the terms of the Mozilla Public License,
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
* obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under
* the terms of the Healthcare Disclaimer located at http://openmrs.org/license.
*
* Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS
* graphic logo is a trademark of OpenMRS Inc.
*/
package org.openmrs.api.context;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.Future;
import javax.mail.Authenticator;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import org.aopalliance.aop.Advice;
import org.apache.commons.lang.StringUtils;
import org.openmrs.Allergen;
import org.openmrs.GlobalProperty;
import org.openmrs.PersonName;
import org.openmrs.Privilege;
import org.openmrs.Role;
import org.openmrs.User;
import org.openmrs.api.APIException;
import org.openmrs.api.AdministrationService;
import org.openmrs.api.CohortService;
import org.openmrs.api.ConceptService;
import org.openmrs.api.DatatypeService;
import org.openmrs.api.EncounterService;
import org.openmrs.api.FormService;
import org.openmrs.api.LocationService;
import org.openmrs.api.ObsService;
import org.openmrs.api.OpenmrsService;
import org.openmrs.api.OrderService;
import org.openmrs.api.OrderSetService;
import org.openmrs.api.PatientService;
import org.openmrs.api.PersonService;
import org.openmrs.api.ProgramWorkflowService;
import org.openmrs.api.ProviderService;
import org.openmrs.api.SerializationService;
import org.openmrs.api.UserService;
import org.openmrs.api.VisitService;
import org.openmrs.api.db.ContextDAO;
import org.openmrs.hl7.HL7Service;
import org.openmrs.logic.LogicService;
import org.openmrs.messagesource.MessageSourceService;
import org.openmrs.module.ModuleMustStartException;
import org.openmrs.module.ModuleUtil;
import org.openmrs.notification.AlertService;
import org.openmrs.notification.MessageException;
import org.openmrs.notification.MessagePreparator;
import org.openmrs.notification.MessageSender;
import org.openmrs.notification.MessageService;
import org.openmrs.notification.mail.MailMessageSender;
import org.openmrs.notification.mail.velocity.VelocityMessagePreparator;
import org.openmrs.scheduler.SchedulerService;
import org.openmrs.scheduler.SchedulerUtil;
import org.openmrs.util.DatabaseUpdateException;
import org.openmrs.util.DatabaseUpdater;
import org.openmrs.util.InputRequiredException;
import org.openmrs.util.LocaleUtility;
import org.openmrs.util.OpenmrsClassLoader;
import org.openmrs.util.OpenmrsConstants;
import org.openmrs.util.OpenmrsUtil;
import org.openmrs.util.PrivilegeConstants;
import org.openmrs.validator.ValidateUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.Advisor;
/**
* Represents an OpenMRS <code>Context</code>, which may be used to authenticate to the database and
* obtain services in order to interact with the system.<br>
* <br>
* The Context is split into a {@link UserContext} and {@link ServiceContext}. The UserContext is
* lightweight and there is an instance for every user logged into the system. The ServiceContext is
* heavier and it contains each service class. This is more static and there is only one ServiceContext
* per OpenMRS instance. <br>
* <br>
* Both the {@link UserContext} and the {@link ServiceContext} should not be used directly. This
* context class has methods to pass through to the currently defined UserContext for the thread and
* the currently defined ServiceContext. <br>
* <br>
* To use the OpenMRS api there are four things that have to be done:
* <ol>
* <li>Call {@link Context#startup(String, String, String, Properties)} to let the Context contact
* the database</li>
* <li>Call {@link Context#openSession()} to start a "unit of work".</li>
* <li>Call {@link Context#authenticate(String, String)} to authenticate the current user on the
* current thread</li>
* <li>Call {@link Context#closeSession()} to end your "unit of work" and commit all changes to the
* database.</li>
* </ol>
* <br>
* Example usage:
*
* <pre>
* public static void main(String[] args) {
* Context.startup("jdbc:mysql://localhost:3306/db-name?autoReconnect=true", "openmrs-db-user", "3jknfjkn33ijt", new Properties());
* try {
* Context.openSession();
* Context.authenticate("admin", "test");
* List<Patients> patients = Context.getPatientService().getPatientsByName("Fred");
* patients.get(0).setBirthdate(new Date());
* Context.getPatientService().savePatient(patients.get(0));
* ...
* }
* finally {
* Context.closeSession();
* }
* }
* </pre>
*
* @see org.openmrs.api.context.UserContext
* @see org.openmrs.api.context.ServiceContext
*/
public class Context {
private static final Logger log = LoggerFactory.getLogger(Context.class);
// Global resources
private static ContextDAO contextDAO;
private static Session mailSession;
// Using "wrapper" (Object array) around UserContext to avoid ThreadLocal
// bug in Java 1.5
private static final ThreadLocal<Object[] /* UserContext */> userContextHolder = new ThreadLocal<Object[] /* UserContext */>();
private static ServiceContext serviceContext;
private static Properties runtimeProperties = new Properties();
private static Properties configProperties = new Properties();
/**
* Default public constructor
*/
public Context() {
}
/**
* Gets the context's data access object
*
* @return ContextDAO
*/
static ContextDAO getContextDAO() {
if (contextDAO == null) {
throw new APIException("error.context.null", (Object[]) null);
}
return contextDAO;
}
/**
* Used to set the context's DAO for the application.
*
* @param dao ContextDAO to set
*/
public void setContextDAO(ContextDAO dao) {
setDAO(dao);
}
public static void setDAO(ContextDAO dao) {
contextDAO = dao;
}
/**
* Loads a class with an instance of the OpenmrsClassLoader. Convenience method equivalent to
* OpenmrsClassLoader.getInstance().loadClass(className);
*
* @param className the class to load
* @return the class that was loaded
* @throws ClassNotFoundException
* @should load class with the OpenmrsClassLoader
*/
public static Class<?> loadClass(String className) throws ClassNotFoundException {
return OpenmrsClassLoader.getInstance().loadClass(className);
}
/**
* Sets the user context on the thread local so that the service layer can perform
* authentication/authorization checks.<br>
* <br>
* This is thread safe since it stores the given user context in ThreadLocal.
*
* @param ctx UserContext to set
*/
public static void setUserContext(UserContext ctx) {
if (log.isTraceEnabled()) {
log.trace("Setting user context " + ctx);
}
Object[] arr = new Object[] { ctx };
userContextHolder.set(arr);
}
/**
* Clears the user context from the threadlocal.
*/
public static void clearUserContext() {
if (log.isTraceEnabled()) {
log.trace("Clearing user context " + userContextHolder.get());
}
userContextHolder.remove();
}
/**
* Gets the user context from the thread local. This might be accessed by several threads at the
* same time.
*
* @return The current UserContext for this thread.
* @should fail if session hasn't been opened
*/
public static UserContext getUserContext() {
Object[] arr = userContextHolder.get();
if (log.isTraceEnabled()) {
log.trace("Getting user context " + Arrays.toString(arr) + " from userContextHolder " + userContextHolder);
}
if (arr == null) {
log.trace("userContext is null.");
throw new APIException(
"A user context must first be passed to setUserContext()...use Context.openSession() (and closeSession() to prevent memory leaks!) before using the API");
}
return (UserContext) userContextHolder.get()[0];
}
/**
* Gets the currently defined service context. If one is not defined, one will be created and
* then returned.
*
* @return the current ServiceContext
*/
static ServiceContext getServiceContext() {
if (serviceContext == null) {
log.error("serviceContext is null. Creating new ServiceContext()");
serviceContext = ServiceContext.getInstance();
}
if (log.isTraceEnabled()) {
log.trace("serviceContext: " + serviceContext);
}
return ServiceContext.getInstance();
}
/**
* Sets the service context.
*
* @param ctx
*/
public void setServiceContext(ServiceContext ctx) {
setContext(ctx);
}
public static void setContext(ServiceContext ctx) {
serviceContext = ctx;
}
/**
* Used to authenticate user within the context
*
* @param username user's identifier token for login
* @param password user's password for authenticating to context
* @throws ContextAuthenticationException
* @should not authenticate with null username and password
* @should not authenticate with null password
* @should not authenticate with null username
* @should not authenticate with null password and proper username
* @should not authenticate with null password and proper system id
*/
public static void authenticate(String username, String password) throws ContextAuthenticationException {
if (log.isDebugEnabled()) {
log.debug("Authenticating with username: " + username);
}
if (Daemon.isDaemonThread()) {
log.error("Authentication attempted while operating on a "
+ "daemon thread, authenticating is not necessary or allowed");
return;
}
getUserContext().authenticate(username, password, getContextDAO());
}
/**
* Refresh the authenticated user object in the current UserContext. This should be used when
* updating information in the database about the current user and it needs to be reflecting in
* the (cached) {@link #getAuthenticatedUser()} User object.
*
* @since 1.5
* @should get fresh values from the database
*/
public static void refreshAuthenticatedUser() {
if (Daemon.isDaemonThread()) {
return;
}
if (log.isDebugEnabled()) {
log.debug("Refreshing authenticated user");
}
getUserContext().refreshAuthenticatedUser();
}
/**
* Become a different user. (You should only be able to do this as a superuser.)
*
* @param systemId
* @throws ContextAuthenticationException
* @should change locale when become another user
*/
public static void becomeUser(String systemId) throws ContextAuthenticationException {
if (log.isInfoEnabled()) {
log.info("systemId: " + systemId);
}
User user = getUserContext().becomeUser(systemId);
// if assuming identity procedure finished successfully, we should change context locale parameter
Locale locale = null;
if (user.getUserProperties().containsKey(OpenmrsConstants.USER_PROPERTY_DEFAULT_LOCALE)) {
String localeString = user.getUserProperty(OpenmrsConstants.USER_PROPERTY_DEFAULT_LOCALE);
locale = LocaleUtility.fromSpecification(localeString);
}
// when locale parameter is not valid or does not exist
if (locale == null) {
locale = LocaleUtility.getDefaultLocale();
}
Context.setLocale(locale);
}
/**
* Get the runtime properties that this OpenMRS instance was started with
*
* @return copy of the runtime properties
*/
public static Properties getRuntimeProperties() {
if (log.isTraceEnabled()) {
log.trace("getting runtime properties. size: " + runtimeProperties.size());
}
Properties props = new Properties();
props.putAll(runtimeProperties);
return props;
}
/**
* Set the runtime properties to be used by this OpenMRS instance
*
* @param props runtime properties
*/
public static void setRuntimeProperties(Properties props) {
runtimeProperties = props;
}
/**
* @return concept dictionary-related services
*/
public static ConceptService getConceptService() {
return getServiceContext().getConceptService();
}
/**
* @return encounter-related services
*/
public static EncounterService getEncounterService() {
return getServiceContext().getEncounterService();
}
/**
* @return location services
*/
public static LocationService getLocationService() {
return getServiceContext().getLocationService();
}
/**
* @return observation services
*/
public static ObsService getObsService() {
return getServiceContext().getObsService();
}
/**
* @return patient-related services
*/
public static PatientService getPatientService() {
return getServiceContext().getPatientService();
}
public static CohortService getCohortService() {
return getServiceContext().getCohortService();
}
/**
* @return person-related services
*/
public static PersonService getPersonService() {
return getServiceContext().getPersonService();
}
/**
* @return Returns the hl7Service.
*/
public static HL7Service getHL7Service() {
return getServiceContext().getHL7Service();
}
/**
* @return user-related services
*/
public static UserService getUserService() {
return getServiceContext().getUserService();
}
/**
* @return order service
*/
public static OrderService getOrderService() {
return getServiceContext().getOrderService();
}
/**
* @return orderSet service
* @since 1.12
*/
public static OrderSetService getOrderSetService() {
return getServiceContext().getOrderSetService();
}
/**
* @return form service
*/
public static FormService getFormService() {
return getServiceContext().getFormService();
}
/**
* @return serialization service
* @since 1.5
*/
public static SerializationService getSerializationService() {
return getServiceContext().getSerializationService();
}
/**
* @return logic service
*/
public static LogicService getLogicService() {
return getServiceContext().getLogicService();
}
/**
* @return admin-related services
*/
public static AdministrationService getAdministrationService() {
return getServiceContext().getAdministrationService();
}
/**
* @return MessageSourceService
*/
public static MessageSourceService getMessageSourceService() {
return getServiceContext().getMessageSourceService();
}
/**
* @return scheduler service
*/
public static SchedulerService getSchedulerService() {
return getServiceContext().getSchedulerService();
}
/**
* @return alert service
*/
public static AlertService getAlertService() {
return getServiceContext().getAlertService();
}
/**
* @return program- and workflow-related services
*/
public static ProgramWorkflowService getProgramWorkflowService() {
return getServiceContext().getProgramWorkflowService();
}
/**
* Get the message service.
*
* @return message service
*/
public static MessageService getMessageService() {
MessageService ms = getServiceContext().getMessageService();
try {
// Message service dependencies
if (ms.getMessagePreparator() == null) {
ms.setMessagePreparator(getMessagePreparator());
}
if (ms.getMessageSender() == null) {
ms.setMessageSender(getMessageSender());
}
}
catch (Exception e) {
log.error("Unable to create message service due", e);
}
return ms;
}
/**
* Gets the mail session required by the mail message service. This function forces
* authentication via the getAdministrationService() method call
*
* @return a java mail session
*/
private static Session getMailSession() {
if (mailSession == null) {
AdministrationService adminService = getAdministrationService();
Properties props = new Properties();
props.setProperty("mail.transport.protocol", adminService.getGlobalProperty("mail.transport_protocol"));
props.setProperty("mail.smtp.host", adminService.getGlobalProperty("mail.smtp_host"));
props.setProperty("mail.smtp.port", adminService.getGlobalProperty("mail.smtp_port"));
props.setProperty("mail.from", adminService.getGlobalProperty("mail.from"));
props.setProperty("mail.debug", adminService.getGlobalProperty("mail.debug"));
props.setProperty("mail.smtp.auth", adminService.getGlobalProperty("mail.smtp_auth"));
Authenticator auth = new Authenticator() {
@Override
public PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(getAdministrationService().getGlobalProperty("mail.user"),
getAdministrationService().getGlobalProperty("mail.password"));
}
};
mailSession = Session.getInstance(props, auth);
}
return mailSession;
}
/**
* Convenience method to allow us to change the configuration more easily. TODO Ideally, we
* would be using Spring's method injection to set the dependencies for the message service.
*
* @return the ServiceContext
*/
private static MessageSender getMessageSender() {
return new MailMessageSender(getMailSession());
}
/**
* Convenience method to allow us to change the configuration more easily. TODO See todo for
* message sender.
*
* @return
*/
private static MessagePreparator getMessagePreparator() throws MessageException {
return new VelocityMessagePreparator();
}
/**
* @return "active" user who has been authenticated, otherwise <code>null</code>
*/
public static User getAuthenticatedUser() {
if (Daemon.isDaemonThread()) {
return Daemon.getDaemonThreadUser();
}
return getUserContext().getAuthenticatedUser();
}
/**
* @return true if user has been authenticated in this context
*/
public static boolean isAuthenticated() {
if (Daemon.isDaemonThread()) {
return true;
} else {
return getAuthenticatedUser() != null;
}
}
/**
* logs out the "active" (authenticated) user within context
*
* @see #authenticate
* @should not fail if session hasn't been opened yet
*/
public static void logout() {
if (!isSessionOpen()) {
return; // fail early if there isn't even a session open
}
if (log.isDebugEnabled()) {
log.debug("Logging out : " + getAuthenticatedUser());
}
getUserContext().logout();
// reset the UserContext object (usually cleared out by closeSession()
// soon after this)
setUserContext(new UserContext());
}
/**
* Convenience method. Passes through to userContext.getAllRoles(User)
*/
public static Set<Role> getAllRoles(User user) throws Exception {
return getUserContext().getAllRoles();
}
/**
* Convenience method. Passes through to userContext.hasPrivilege(String)
*
* @should give daemon user full privileges
*/
public static boolean hasPrivilege(String privilege) {
// the daemon threads have access to all things
if (Daemon.isDaemonThread()) {
return true;
}
return getUserContext().hasPrivilege(privilege);
}
/**
* Throws an exception if the currently authenticated user does not have the specified
* privilege.
*
* @param privilege
* @throws ContextAuthenticationException
*/
public static void requirePrivilege(String privilege) throws ContextAuthenticationException {
if (!hasPrivilege(privilege)) {
String errorMessage = null;
if (StringUtils.isNotBlank(privilege)) {
errorMessage = Context.getMessageSourceService().getMessage("error.privilegesRequired",
new Object[] { privilege }, null);
} else {
//Should we even be here if the privilege is blank?
errorMessage = Context.getMessageSourceService().getMessage("error.privilegesRequiredNoArgs");
}
throw new ContextAuthenticationException(errorMessage);
}
}
/**
* Convenience method. Passes through to {@link UserContext#addProxyPrivilege(String)}
*/
public static void addProxyPrivilege(String privilege) {
getUserContext().addProxyPrivilege(privilege);
}
/**
* Convenience method. Passes through to {@link UserContext#removeProxyPrivilege(String)}
*/
public static void removeProxyPrivilege(String privilege) {
getUserContext().removeProxyPrivilege(privilege);
}
/**
* Convenience method. Passes through to {@link UserContext#setLocale(Locale)}
*/
public static void setLocale(Locale locale) {
getUserContext().setLocale(locale);
}
/**
* Convenience method. Passes through to {@link UserContext#getLocale()}
*
* @should not fail if session hasn't been opened
*/
public static Locale getLocale() {
// if a session hasn't been opened, just fetch the default
if (!isSessionOpen()) {
return LocaleUtility.getDefaultLocale();
}
return getUserContext().getLocale();
}
/**
* Used to define a unit of work. All "units of work" should be surrounded by openSession and
* closeSession calls.
*/
public static void openSession() {
log.trace("opening session");
setUserContext(new UserContext()); // must be cleared out in
// closeSession()
getContextDAO().openSession();
}
/**
* Used to define a unit of work. All "units of work" should be surrounded by openSession and
* closeSession calls.
*/
public static void closeSession() {
log.trace("closing session");
clearUserContext(); // because we set a UserContext on the current
// thread in openSession()
getContextDAO().closeSession();
}
/**
* Used to define a unit of work which does not require clearing out the currently authenticated
* user. Remember to call closeSessionWithCurrentUser in a, preferably, finally block after this
* work.
*
* @since 1.10
*/
public static void openSessionWithCurrentUser() {
getContextDAO().openSession();
}
/**
* Used when the a unit of work which started with a call for openSessionWithCurrentUser has
* finished. This should be in a, preferably, finally block.
*
* @since 1.10
*/
public static void closeSessionWithCurrentUser() {
getContextDAO().closeSession();
}
/**
* Clears cached changes made so far during this unit of work without writing them to the
* database. If you call this method, and later call closeSession() or flushSession() your
* changes are still lost.
*/
public static void clearSession() {
log.trace("clearing session");
getContextDAO().clearSession();
}
/**
* Forces any changes made so far in this unit of work to be written to the database
*
* @since 1.6
*/
public static void flushSession() {
log.trace("flushing session");
getContextDAO().flushSession();
}
/**
* This method tells whether {@link #openSession()} has been called or not already. If it hasn't
* been called, some methods won't work correctly because a {@link UserContext} isn't available.
*
* @return true if {@link #openSession()} has been called already.
* @since 1.5
* @should return true if session is closed
*/
public static boolean isSessionOpen() {
return userContextHolder.get() != null;
}
/**
* Used to re-read the state of the given instance from the underlying database.
* @since 2.0
* @param obj The object to refresh from the database in the session
*/
public static void refreshEntity(Object obj) {
log.trace("refreshing object: "+obj);
getContextDAO().refreshEntity(obj);
}
/**
* Used to clear a cached object out of a session in the middle of a unit of work. Future
* updates to this object will not be saved. Future gets of this object will not fetch this
* cached copy
*
* @param obj The object to evict/remove from the session
*/
public static void evictFromSession(Object obj) {
log.trace("clearing session");
getContextDAO().evictFromSession(obj);
}
/**
* Starts the OpenMRS System Should be called prior to any kind of activity
*
* @param props Runtime properties to use for startup
* @throws InputRequiredException if the {@link DatabaseUpdater} has determined that updates
* cannot continue without input from the user
* @throws DatabaseUpdateException if database updates are required, see
* {@link DatabaseUpdater#executeChangelog()}
* @throws ModuleMustStartException if a module that should be started is not able to
* @see InputRequiredException#getRequiredInput() InputRequiredException#getRequiredInput() for
* the required question/datatypes
*/
public synchronized static void startup(Properties props) throws DatabaseUpdateException, InputRequiredException,
ModuleMustStartException {
// do any context database specific startup
getContextDAO().startup(props);
// find/set/check whether the current database version is compatible
checkForDatabaseUpdates(props);
// this should be first in the startup routines so that the application
// data directory can be set from the runtime properties
OpenmrsUtil.startup(props);
openSession();
clearSession();
// add any privileges/roles that /must/ exist for openmrs to work
// correctly.
checkCoreDataset();
getContextDAO().setupSearchIndex();
// Loop over each module and startup each with these custom properties
ModuleUtil.startup(props);
}
/**
* Starts the OpenMRS System in a _non-webapp_ environment<br>
* <br>
* <b>Note:</b> This method calls {@link Context#openSession()}, so you must call
* {@link Context#closeSession()} somewhere on the same thread of this application so as to not
* leak memory.
*
* @param url database url like "jdbc:mysql://localhost:3306/openmrs?autoReconnect=true"
* @param username Connection username
* @param password Connection password
* @param properties Other startup properties
* @throws InputRequiredException if the {@link DatabaseUpdater} has determined that updates
* cannot continue without input from the user
* @throws DatabaseUpdateException if the database must be updated. See {@link DatabaseUpdater}
* @throws ModuleMustStartException if a module that should start is not able to
* @see #startup(Properties)
* @see InputRequiredException#getRequiredInput() InputRequiredException#getRequiredInput() for
* the required question/datatypes
*/
public synchronized static void startup(String url, String username, String password, Properties properties)
throws DatabaseUpdateException, InputRequiredException, ModuleMustStartException {
if (properties == null) {
properties = new Properties();
}
properties.put("connection.url", url);
properties.put("connection.username", username);
properties.put("connection.password", password);
setRuntimeProperties(properties);
openSession(); // so that the startup method can use proxyPrivileges
startup(properties);
// start the scheduled tasks
SchedulerUtil.startup(properties);
closeSession();
}
/**
* Stops the OpenMRS System Should be called after all activity has ended and application is
* closing
*/
public static void shutdown() {
log.debug("Shutting down the scheduler");
try {
// Needs to be shutdown before Hibernate
SchedulerUtil.shutdown();
}
catch (Exception e) {
log.warn("Error while shutting down scheduler service", e);
}
log.debug("Shutting down the modules");
try {
ModuleUtil.shutdown();
}
catch (Exception e) {
log.warn("Error while shutting down module system", e);
}
log.debug("Shutting down the context");
try {
ContextDAO dao = null;
try {
dao = getContextDAO();
}
catch (APIException e) {
// pass
}
if (dao != null) {
dao.shutdown();
}
}
catch (Exception e) {
log.warn("Error while shutting down context dao", e);
}
}
/**
* Used for getting services not in the previous get*Service() calls
*
* @param cls The Class of the service to get
* @return The requested Service
* @should return the same object when called multiple times for the same class
*/
public static <T extends Object> T getService(Class<? extends T> cls) {
return getServiceContext().getService(cls);
}
/**
* Adds an AOP advisor around the given Class <code>cls</code>
* <p>
* Advisors can wrap around a method and effect the method before or after
*
* @param cls
* @param advisor
*/
public static void addAdvisor(Class cls, Advisor advisor) {
getServiceContext().addAdvisor(cls, advisor);
}
/**
* Adds an AOP advice object around the given Class <code>cls</code>
* <p>
* Advice comes in the form of before or afterReturning methods
*
* @param cls
* @param advice
*/
public static void addAdvice(Class cls, Advice advice) {
getServiceContext().addAdvice(cls, advice);
}
/**
* Removes the given AOP advisor from Class <code>cls</code>
*
* @param cls
* @param advisor
*/
public static void removeAdvisor(Class cls, Advisor advisor) {
getServiceContext().removeAdvisor(cls, advisor);
}
/**
* Removes the given AOP advice object from Class <code>cls</code>
*
* @param cls
* @param advice
*/
public static void removeAdvice(Class cls, Advice advice) {
getServiceContext().removeAdvice(cls, advice);
}
/**
* Runs through the core data (e.g. privileges, roles, and global properties) and adds them if
* necessary.
*/
public static void checkCoreDataset() {
// setting core roles
try {
Context.addProxyPrivilege(PrivilegeConstants.MANAGE_ROLES);
Set<String> currentRoleNames = new HashSet<String>();
for (Role role : Context.getUserService().getAllRoles()) {
currentRoleNames.add(role.getRole().toUpperCase());
}
Map<String, String> map = OpenmrsUtil.getCoreRoles();
for (Map.Entry<String, String> entry : map.entrySet()) {
String roleName = entry.getKey();
if (!currentRoleNames.contains(roleName.toUpperCase())) {
Role role = new Role();
role.setRole(roleName);
role.setDescription(entry.getValue());
Context.getUserService().saveRole(role);
}
}
}
catch (Exception e) {
log.error("Error while setting core roles for openmrs system", e);
}
finally {
Context.removeProxyPrivilege(PrivilegeConstants.MANAGE_ROLES);
}
// setting core privileges
try {
Context.addProxyPrivilege(PrivilegeConstants.MANAGE_PRIVILEGES);
Set<String> currentPrivilegeNames = new HashSet<String>();
for (Privilege privilege : Context.getUserService().getAllPrivileges()) {
currentPrivilegeNames.add(privilege.getPrivilege().toUpperCase());
}
Map<String, String> map = OpenmrsUtil.getCorePrivileges();
for (Map.Entry<String, String> entry : map.entrySet()) {
String privilegeName = entry.getKey();
if (!currentPrivilegeNames.contains(privilegeName.toUpperCase())) {
Privilege p = new Privilege();
p.setPrivilege(privilegeName);
p.setDescription(entry.getValue());
Context.getUserService().savePrivilege(p);
}
}
}
catch (Exception e) {
log.error("Error while setting core privileges", e);
}
finally {
Context.removeProxyPrivilege(PrivilegeConstants.MANAGE_PRIVILEGES);
}
// setting core global properties
try {
Context.addProxyPrivilege(PrivilegeConstants.MANAGE_GLOBAL_PROPERTIES);
Context.addProxyPrivilege(PrivilegeConstants.GET_GLOBAL_PROPERTIES);
Set<String> currentPropNames = new HashSet<String>();
Map<String, GlobalProperty> propsMissingDescription = new HashMap<String, GlobalProperty>();
Map<String, GlobalProperty> propsMissingDatatype = new HashMap<String, GlobalProperty>();
for (GlobalProperty prop : Context.getAdministrationService().getAllGlobalProperties()) {
currentPropNames.add(prop.getProperty().toUpperCase());
if (prop.getDescription() == null) {
propsMissingDescription.put(prop.getProperty().toUpperCase(), prop);
}
if (prop.getDatatypeClassname() == null) {
propsMissingDatatype.put(prop.getProperty().toUpperCase(), prop);
}
}
for (GlobalProperty coreProp : OpenmrsConstants.CORE_GLOBAL_PROPERTIES()) {
String corePropName = coreProp.getProperty().toUpperCase();
// if the prop doesn't exist, save it
if (!currentPropNames.contains(corePropName)) {
Context.getAdministrationService().saveGlobalProperty(coreProp);
currentPropNames.add(corePropName); // add to list in case
// of duplicates
} else {
// if the prop is missing its description, update it
GlobalProperty propToUpdate = propsMissingDescription.get(corePropName);
if (propToUpdate != null) {
propToUpdate.setDescription(coreProp.getDescription());
Context.getAdministrationService().saveGlobalProperty(propToUpdate);
}
// set missing datatypes
propToUpdate = propsMissingDatatype.get(corePropName);
if (propToUpdate != null && coreProp.getDatatypeClassname() != null) {
propToUpdate.setDatatypeClassname(coreProp.getDatatypeClassname());
propToUpdate.setDatatypeConfig(coreProp.getDatatypeConfig());
propToUpdate.setPreferredHandlerClassname(coreProp.getPreferredHandlerClassname());
propToUpdate.setHandlerConfig(coreProp.getHandlerConfig());
Context.getAdministrationService().saveGlobalProperty(propToUpdate);
}
}
}
}
catch (Exception e) {
log.error("Error while setting core global properties", e);
}
finally {
Context.removeProxyPrivilege(PrivilegeConstants.MANAGE_GLOBAL_PROPERTIES);
Context.removeProxyPrivilege(PrivilegeConstants.GET_GLOBAL_PROPERTIES);
}
// setting default validation rule
AdministrationService as = Context.getAdministrationService();
Boolean disableValidation = Boolean.valueOf(as.getGlobalProperty(OpenmrsConstants.GP_DISABLE_VALIDATION, "false"));
ValidateUtil.setDisableValidation(disableValidation);
PersonName.setFormat(Context.getAdministrationService().getGlobalProperty(
OpenmrsConstants.GLOBAL_PROPERTY_LAYOUT_NAME_FORMAT));
Allergen.setOtherNonCodedConceptUuid(Context.getAdministrationService().getGlobalProperty(
OpenmrsConstants.GP_ALLERGEN_OTHER_NON_CODED_UUID));
}
/**
* Runs any needed updates on the current database if the user has the allow_auto_update runtime
* property set to true. If not set to true, then {@link #updateDatabase(Map)} must be called.<br>
* <br>
* If an {@link InputRequiredException} is thrown, a call to {@link #updateDatabase(Map)} is
* required with a mapping from question prompt to user answer.
*
* @param props the runtime properties
* @throws InputRequiredException if the {@link DatabaseUpdater} has determined that updates
* cannot continue without input from the user
* @see InputRequiredException#getRequiredInput() InputRequiredException#getRequiredInput() for
* the required question/datatypes
*/
private static void checkForDatabaseUpdates(Properties props) throws DatabaseUpdateException, InputRequiredException {
boolean updatesRequired = true;
try {
updatesRequired = DatabaseUpdater.updatesRequired();
}
catch (Exception e) {
throw new DatabaseUpdateException("Unable to check if database updates are required", e);
}
// this must be the first thing run in case it changes database mappings
if (updatesRequired) {
if (DatabaseUpdater.allowAutoUpdate()) {
DatabaseUpdater.executeChangelog();
} else {
throw new DatabaseUpdateException(
"Database updates are required. Call Context.updateDatabase() before .startup() to continue.");
}
}
}
/**
* Updates the openmrs database to the latest. This is only needed if using the API alone. <br>
* <br>
* The typical use-case would be: Try to {@link #startup(String, String, String, Properties)},
* if that fails, call this method to get the database up to speed.
*
* @param userInput (can be null) responses from the user about needed input
* @throws DatabaseUpdateException if an error occurred while updating
* @throws InputRequiredException if user input is required
* @since 1.5
*/
public static void updateDatabase(Map<String, Object> userInput) throws DatabaseUpdateException, InputRequiredException {
DatabaseUpdater.executeChangelog(null, userInput);
}
/**
* Gets the simple date format for the current user's locale. The format will be similar in size
* to mm/dd/yyyy
*
* @return SimpleDateFormat for the user's current locale
* @see org.openmrs.util.OpenmrsUtil#getDateFormat(Locale)
* @should return a pattern with four y characters in it
*/
public static SimpleDateFormat getDateFormat() {
return OpenmrsUtil.getDateFormat(getLocale());
}
/**
* Gets the simple time format for the current user's locale. The format will be similar to
* hh:mm a
*
* @return SimpleDateFormat for the user's current locale
* @see org.openmrs.util.OpenmrsUtil#getTimeFormat(Locale)
* @should return a pattern with two h characters in it
*/
public static SimpleDateFormat getTimeFormat() {
return OpenmrsUtil.getTimeFormat(getLocale());
}
/**
* Gets the simple datetime format for the current user's locale. The format will be similar to
* mm/dd/yyyy hh:mm a
*
* @return SimpleDateFormat for the user's current locale
* @see org.openmrs.util.OpenmrsUtil#getDateTimeFormat(Locale)
* @should return a pattern with four y characters and two h characters in it
*/
public static SimpleDateFormat getDateTimeFormat() {
return OpenmrsUtil.getDateTimeFormat(getLocale());
}
/**
* @return true/false whether the service context is currently being refreshed
* @see org.openmrs.api.context.ServiceContext#isRefreshingContext()
*/
public static boolean isRefreshingContext() {
return getServiceContext().isRefreshingContext();
}
/**
* @since 1.5
* @see ServiceContext#getRegisteredComponents(Class)
*/
public static <T extends Object> List<T> getRegisteredComponents(Class<T> type) {
return getServiceContext().getRegisteredComponents(type);
}
/**
* @see ServiceContext#getRegisteredComponent(String, Class)
* @since 1.9.4
*/
public static <T> T getRegisteredComponent(String beanName, Class<T> type) throws APIException {
return getServiceContext().getRegisteredComponent(beanName, type);
}
/**
* @see ServiceContext#getModuleOpenmrsServices(String)
* @since 1.9
*/
public static List<OpenmrsService> getModuleOpenmrsServices(String modulePackage) {
return getServiceContext().getModuleOpenmrsServices(modulePackage);
}
/**
* @since 1.9
* @see ServiceContext#getVisitService()
*/
public static VisitService getVisitService() {
return getServiceContext().getVisitService();
}
/**
* @since 1.9
* @see ServiceContext#getProviderService()
*/
public static ProviderService getProviderService() {
return getServiceContext().getProviderService();
}
/**
* @since 1.9
* @see ServiceContext#getDatatypeService()
*/
public static DatatypeService getDatatypeService() {
return getServiceContext().getDatatypeService();
}
/**
* Add or replace a property in the config properties list
*
* @param key name of the property
* @param value value of the property
* @since 1.9
*/
public static void addConfigProperty(Object key, Object value) {
configProperties.put(key, value);
}
/**
* Remove a property from the list of config properties
*
* @param key name of the property
* @since 1.9
*/
public static void removeConfigProperty(Object key) {
configProperties.remove(key);
}
/**
* Get the config properties that have been added to this OpenMRS instance
*
* @return copy of the module properties
* @since 1.9
*/
public static Properties getConfigProperties() {
Properties props = new Properties();
props.putAll(configProperties);
return props;
}
/**
* Updates the search index. It is a blocking operation, which may take even a few minutes
* depending on the index size.
* <p>
* There is no need to call this method in normal usage since the index is automatically updated
* whenever DB transactions are committed.
* <p>
* The method is designated to be used in tests, which rollback transactions. Note that if the
* transaction is rolled back, changes to the index will not be reverted.
*
* @since 1.11
*/
public static void updateSearchIndex() {
getContextDAO().updateSearchIndex();
}
/**
* Updates the search index. It is an asynchronous operation.
* <p>
* There is no need to call this method in normal usage since the index is automatically updated
* whenever DB transactions are committed.
* <p>
*
* @return object representing the result of the started asynchronous operation
*/
public static Future<?> updateSearchIndexAsync() {
return getContextDAO().updateSearchIndexAsync();
}
/**
* Updates the search index for objects of the given type.
*
* @see #updateSearchIndex()
* @param type
* @since 1.11
*/
public static void updateSearchIndexForType(Class<?> type) {
getContextDAO().updateSearchIndexForType(type);
}
/**
* Updates the search index for the given object.
*
* @see #updateSearchIndex()
* @param object
* @since 1.11
*/
public static void updateSearchIndexForObject(Object object) {
getContextDAO().updateSearchIndexForObject(object);
}
/**
* @see org.openmrs.api.context.ServiceContext#setUseSystemClassLoader(boolean)
* @since 1.10
*/
public static void setUseSystemClassLoader(boolean useSystemClassLoader) {
getServiceContext().setUseSystemClassLoader(useSystemClassLoader);
}
/**
* @see org.openmrs.api.context.ServiceContext#isUseSystemClassLoader()
* @since 1.10
*/
public static boolean isUseSystemClassLoader() {
return getServiceContext().isUseSystemClassLoader();
}
}