org.openmrs.module.webservices.rest.web.v1_0.resource.openmrs1_8.ModuleActionResource1_8.java Source code

Java tutorial

Introduction

Here is the source code for org.openmrs.module.webservices.rest.web.v1_0.resource.openmrs1_8.ModuleActionResource1_8.java

Source

/**
 * 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.module.webservices.rest.web.v1_0.resource.openmrs1_8;

import io.swagger.models.Model;
import io.swagger.models.ModelImpl;
import io.swagger.models.properties.ArrayProperty;
import io.swagger.models.properties.BooleanProperty;
import io.swagger.models.properties.RefProperty;
import io.swagger.models.properties.StringProperty;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.openmrs.api.APIException;
import org.openmrs.module.Module;
import org.openmrs.module.ModuleException;
import org.openmrs.module.ModuleUtil;
import org.openmrs.module.webservices.docs.swagger.core.property.EnumProperty;
import org.openmrs.module.webservices.helper.ModuleAction;
import org.openmrs.module.webservices.helper.ModuleFactoryWrapper;
import org.openmrs.module.webservices.helper.ModuleAction.Action;
import org.openmrs.module.webservices.rest.SimpleObject;
import org.openmrs.module.webservices.rest.web.ConversionUtil;
import org.openmrs.module.webservices.rest.web.RequestContext;
import org.openmrs.module.webservices.rest.web.RestConstants;
import org.openmrs.module.webservices.rest.web.annotation.PropertyGetter;
import org.openmrs.module.webservices.rest.web.annotation.Resource;
import org.openmrs.module.webservices.rest.web.representation.Representation;
import org.openmrs.module.webservices.rest.web.resource.api.Creatable;
import org.openmrs.module.webservices.rest.web.resource.impl.BaseDelegatingResource;
import org.openmrs.module.webservices.rest.web.resource.impl.DelegatingResourceDescription;
import org.openmrs.module.webservices.rest.web.response.IllegalRequestException;
import org.openmrs.module.webservices.rest.web.response.ResourceDoesNotSupportOperationException;
import org.openmrs.module.webservices.rest.web.response.ResponseException;
import org.springframework.util.ResourceUtils;
import javax.servlet.ServletContext;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

@Resource(name = RestConstants.VERSION_1
        + "/moduleaction", supportedClass = ModuleAction.class, supportedOpenmrsVersions = { "1.8.*", "1.9.*",
                "1.10.*", "1.11.*", "1.12.*", "2.0.*", "2.1.*", "2.2.*" })
public class ModuleActionResource1_8 extends BaseDelegatingResource<ModuleAction> implements Creatable {

    /**
     * ModuleFactoryWrapper is used for testing purposes.
     */
    private ModuleFactoryWrapper moduleFactoryWrapper = new ModuleFactoryWrapper();

    public void setModuleFactoryWrapper(ModuleFactoryWrapper moduleFactoryWrapper) {
        this.moduleFactoryWrapper = moduleFactoryWrapper;
    }

    /**
     * Overriding create directly, because ModuleFactory requires ServletContext to execute any
     * action
     */
    @Override
    public Object create(SimpleObject post, RequestContext context) throws ResponseException {
        moduleFactoryWrapper.checkPrivilege();

        ModuleAction action = newDelegate();
        setConvertedProperties(action, post, getCreatableProperties(), true);
        String installUri = action.getInstallUri();

        Collection<Module> modules;
        if (action.isAllModules() != null && action.isAllModules()) {
            modules = moduleFactoryWrapper.getLoadedModules();
            action.setModules(new ArrayList<Module>(modules));
        } else {
            modules = action.getModules();
        }

        ServletContext servletContext = getServletContext(context);

        if (modules == null || modules.isEmpty()) {
            throw new IllegalRequestException(
                    "Cannot execute action " + action.getAction() + " on empty set of modules.");
        } else {
            if (action.getAction() == Action.INSTALL) {
                if (installUri == null || !ResourceUtils.isUrl(installUri)) {
                    throw new IllegalRequestException(
                            "The installUri needs to be a URL for this action to be performed");
                }
            } else {
                if (action.isAllModules() == null || !action.isAllModules()) {
                    // ensure all specified modules exist
                    // ensure they're not trying to modify the REST module
                    for (Module module : modules) {
                        // if they specified a module that's not loaded, it will show up here as null
                        if (module == null) {
                            throw new IllegalRequestException(
                                    "One or more of the modules you specified are not loaded on this server");
                        }
                        if (module.getModuleId().equals(RestConstants.MODULE_ID)) {
                            throw new IllegalRequestException("You are not allowed to modify "
                                    + module.getModuleId() + " via this REST call");
                        }
                    }
                }

                // even if they said allModule=true, don't touch the REST module
                Module restModule = moduleFactoryWrapper.getModuleById(RestConstants.MODULE_ID);
                modules.remove(restModule);
            }

            switch (action.getAction()) {
            case START:
                startModules(modules, servletContext);
                break;
            case STOP:
                stopModules(modules, servletContext, true);
                break;
            case RESTART:
                restartModules(modules, servletContext);
                break;
            case UNLOAD:
                unloadModules(modules, servletContext);
                break;
            case INSTALL:
                Module module = installModule(modules, installUri, servletContext);
                modules.clear();
                modules.add(module);
                action.setModules(new ArrayList<Module>(modules));
                break;
            }
        }

        return ConversionUtil.convertToRepresentation(action, Representation.DEFAULT);
    }

    private Module installModule(Collection<Module> modules, String installUri, ServletContext servletContext) {
        List<Module> moduleList = new ArrayList<Module>(modules);
        List<Module> dependentModulesStopped = new ArrayList<Module>();
        Module existingModule = moduleList.get(0);
        Module tempModule = null;
        File moduleFile = null;

        try {
            if (existingModule != null) {
                dependentModulesStopped = moduleFactoryWrapper.stopModuleAndGetDependent(existingModule);
                for (Module depMod : dependentModulesStopped) {
                    moduleFactoryWrapper.stopModuleSkipRefresh(depMod, servletContext);
                }

                moduleFactoryWrapper.stopModuleSkipRefresh(existingModule, servletContext);
                moduleFactoryWrapper.unloadModule(existingModule);
            }

            URL downloadUrl = new URL(installUri);
            String fileName = FilenameUtils.getName(downloadUrl.getPath());
            InputStream inputStream = ModuleUtil.getURLStream(downloadUrl);
            moduleFile = ModuleUtil.insertModuleFile(inputStream, fileName);
            tempModule = moduleFactoryWrapper.loadModule(moduleFile);
            moduleFactoryWrapper.startModule(tempModule, servletContext);
            if (existingModule != null && dependentModulesStopped.size() > 0
                    && moduleFactoryWrapper.isModuleStarted(tempModule)) {
                startModules(dependentModulesStopped, servletContext);
            }
            return tempModule;
        } catch (MalformedURLException e) {
            throw new APIException(e.getMessage(), e);
        } catch (IOException e) {
            throw new APIException(e.getMessage(), e);
        } finally {
            if (tempModule == null && moduleFile != null) {
                FileUtils.deleteQuietly(moduleFile);
            }
        }
    }

    private void restartModules(Collection<Module> modules, ServletContext servletContext) {
        stopModules(modules, servletContext, false);
        startModules(modules, servletContext);
    }

    private void unloadModules(Collection<Module> modules, ServletContext servletContext) {
        boolean needsRefresh = false;
        for (Module module : modules) {
            if (moduleFactoryWrapper.isModuleStarted(module)) {
                moduleFactoryWrapper.stopModuleSkipRefresh(module, servletContext);
                needsRefresh = true;
            }
            moduleFactoryWrapper.unloadModule(module);
        }

        if (needsRefresh) {
            moduleFactoryWrapper.refreshWebApplicationContext(servletContext);
        }
    }

    /**
     * @param modules modules to stop
     * @param servletContext ServletContext is required by WebModuleUtil to perform operation
     */
    private void stopModules(Collection<Module> modules, ServletContext servletContext, boolean refreshContext) {
        for (Module module : modules) {
            if (moduleFactoryWrapper.isModuleStarted(module)) {
                moduleFactoryWrapper.stopModuleSkipRefresh(module, servletContext);
            }
        }
        if (refreshContext) {
            moduleFactoryWrapper.refreshWebApplicationContext(servletContext);
        }
    }

    /**
     * @param modules modules to start
     * @param servletContext ServletContext is required by WebModuleUtil to perform operation
     */
    private void startModules(Collection<Module> modules, ServletContext servletContext) {
        boolean needsRefresh = false;
        if (modules.size() > 1) {
            modules = moduleFactoryWrapper.getModulesInStartupOrder(modules);
        }

        for (Module module : modules) {
            if (moduleFactoryWrapper.isModuleStopped(module)) {
                needsRefresh = moduleFactoryWrapper.startModuleSkipRefresh(module, servletContext) || needsRefresh;
            }
        }
        //check if any module has been started, doesn't refresh WAC if all modules failed to start
        if (needsRefresh) {
            moduleFactoryWrapper.refreshWebApplicationContext(servletContext);
        }

        findAndThrowStartupErrors(modules);
    }

    private void findAndThrowStartupErrors(Collection<Module> modules) {
        List<Exception> errors = new ArrayList<Exception>();
        for (Module module : modules) {
            if (moduleFactoryWrapper.isModuleStopped(module)) {
                //module actions are executed in other thread, so we need to explicitly check and throw them
                if (module.getStartupErrorMessage() != null) {
                    errors.add(new ModuleException(module.getStartupErrorMessage()));
                }
            }
        }

        if (!errors.isEmpty()) {
            StringBuilder stringBuilder = new StringBuilder();
            for (Exception error : errors) {
                stringBuilder.append(error.getMessage()).append("; ");
            }
            throw new ModuleException(stringBuilder.toString());
        }
    }

    @Override
    public ModuleAction newDelegate() {
        return new ModuleAction();
    }

    @Override
    public ModuleAction save(ModuleAction delegate) {
        throw new UnsupportedOperationException("ModuleAction cannot be saved");
    }

    @Override
    public ModuleAction getByUniqueId(String uniqueId) {
        throw new ResourceDoesNotSupportOperationException();
    }

    @Override
    protected void delete(ModuleAction delegate, String reason, RequestContext context) throws ResponseException {
        throw new ResourceDoesNotSupportOperationException();
    }

    @Override
    public void purge(ModuleAction delegate, RequestContext context) throws ResponseException {
        throw new ResourceDoesNotSupportOperationException();
    }

    @Override
    public DelegatingResourceDescription getRepresentationDescription(Representation rep) {
        DelegatingResourceDescription description = new DelegatingResourceDescription();
        description.addProperty("modules", Representation.REF);
        description.addProperty("action", "action");
        return description;
    }

    @Override
    public DelegatingResourceDescription getCreatableProperties() throws ResourceDoesNotSupportOperationException {
        DelegatingResourceDescription description = new DelegatingResourceDescription();
        description.addProperty("modules");
        description.addProperty("allModules");
        description.addProperty("installUri");
        description.addRequiredProperty("action", "action");
        return description;
    }

    @Override
    public Model getGETModel(Representation rep) {
        return ((ModelImpl) super.getGETModel(rep))
                .property("modules", new ArrayProperty(new RefProperty("#/definitions/ModuleGetRef")))
                .property("action", new EnumProperty(ModuleAction.Action.class));
    }

    @Override
    public Model getCREATEModel(Representation rep) {
        return new ModelImpl().property("modules", new ArrayProperty(new StringProperty().example("moduleId")))
                .property("allModules", new BooleanProperty())
                .property("action", new EnumProperty(ModuleAction.Action.class))
                .property("installUri", new StringProperty()).required("action");
    }

    /**
     * Converter does not handle getters starting with 'is' instead of 'get'
     */
    @PropertyGetter("allModules")
    public Boolean isAllModules(ModuleAction action) {
        return action.isAllModules();
    }

    private ServletContext getServletContext(RequestContext context) {
        return context.getRequest().getSession().getServletContext();
    }
}