All Downloads are FREE. Search and download functionalities are using the official Maven repository.

systems.dmx.config.ConfigPlugin Maven / Gradle / Ivy

There is a newer version: 5.3.4
Show newest version
package systems.dmx.config;

import static systems.dmx.config.Constants.*;
import static systems.dmx.core.Constants.*;
import systems.dmx.core.Assoc;
import systems.dmx.core.RelatedTopic;
import systems.dmx.core.Topic;
import systems.dmx.core.model.TopicModel;
import systems.dmx.core.osgi.PluginActivator;
import systems.dmx.core.service.DirectivesResponse;
import systems.dmx.core.service.Transactional;
import systems.dmx.core.service.accesscontrol.PrivilegedAccess;
import systems.dmx.core.service.event.PostCreateTopic;

import org.codehaus.jettison.json.JSONArray;
import org.codehaus.jettison.json.JSONObject;

import javax.servlet.http.HttpServletRequest;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.PUT;
import javax.ws.rs.core.Context;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;



@Path("/config")
@Produces("application/json")
public class ConfigPlugin extends PluginActivator implements ConfigService, PostCreateTopic {

    // ---------------------------------------------------------------------------------------------- Instance Variables

    /**
     * Key: the "configurable URI" as a config target's hash key, that is either "topicUri:{uri}" or "typeUri:{uri}".
     */
    private Map> registry = new HashMap();

    @Context
    private HttpServletRequest request;

    private Logger logger = Logger.getLogger(getClass().getName());

    // -------------------------------------------------------------------------------------------------- Public Methods

    // --- ConfigService ---

    @GET
    @Override
    public ConfigDefs getConfigDefs() {
        try {
            JSONObject json = new JSONObject();
            PrivilegedAccess pa = dmx.getPrivilegedAccess();
            for (String configurableUri: registry.keySet()) {
                JSONArray array = new JSONArray();
                for (ConfigDef configDef : lookupConfigDefs(configurableUri)) {
                    String username = pa.getUsername(request);
                    long workspaceId = workspaceId(configDef.getConfigModRole());
                    if (pa.hasReadPermission(username, workspaceId)) {
                        array.put(configDef.getConfigTypeUri());
                    }
                }
                if (array.length() > 0) {
                    json.put(configurableUri, array);
                }
            }
            return new ConfigDefs(json);
        } catch (Exception e) {
            throw new RuntimeException("Retrieving the registered config definitions failed", e);
        }
    }

    @GET
    @Path("/{configTypeUri}/topic/{topicId}")
    @Override
    public RelatedTopic getConfigTopic(@PathParam("configTypeUri") String configTypeUri,
                                       @PathParam("topicId") long topicId) {
        return _getConfigTopic(configTypeUri, topicId);
    }

    @PUT
    @Path("/topic/{topicId}")
    @Transactional
    @Override
    public DirectivesResponse updateConfigTopic(@PathParam("topicId") long topicId, TopicModel updateModel) {
        try {
            Topic topic = dmx.getTopic(topicId);
            String configTypeUri = updateModel.getTypeUri();
            RelatedTopic oldConfigTopic = getConfigTopic(configTypeUri, topicId);
            ConfigDef configDef = getApplicableConfigDef(topic, configTypeUri);
            long workspaceId = workspaceId(configDef.getConfigModRole());
            dmx.getPrivilegedAccess().runInWorkspaceContext(workspaceId, () -> {
                Topic configTopic = dmx.createTopic(updateModel);
                if (!configTopic.equals(oldConfigTopic)) {
                    logger.info("change " + oldConfigTopic.getId() + " -> " + configTopic.getId());
                    oldConfigTopic.getRelatingAssoc().delete();
                    createConfigAssoc(topic, configTopic);
                } else {
                    logger.info("no change");
                }
                return null;
            });
            return new DirectivesResponse();
        } catch (Exception e) {
            throw new RuntimeException("Updating config for topic " + topicId + " failed", e);
        }
    }

    @Override
    public void createConfigTopic(String configTypeUri, Topic topic) {
        _createConfigTopic(getApplicableConfigDef(topic, configTypeUri), topic);
    }

    // ---

    @Override
    public void registerConfigDef(ConfigDef configDef) {
        try {
            if (isRegistered(configDef)) {
                throw new RuntimeException("A definition for config type \"" + configDef.getConfigTypeUri() +
                    "\" is already registered");
            }
            //
            String hashKey = configDef.getHashKey();
            List configDefs = lookupConfigDefs(hashKey);
            if (configDefs == null) {
                configDefs = new ArrayList();
                registry.put(hashKey, configDefs);
            }
            configDefs.add(configDef);
        } catch (Exception e) {
            throw new RuntimeException("Registering a config definition failed", e);
        }
    }

    @Override
    public void unregisterConfigDef(String configTypeUri) {
        try {
            for (List configDefs : registry.values()) {
                ConfigDef configDef = findByConfigTypeUri(configDefs, configTypeUri);
                if (configDef != null) {
                    if (!configDefs.remove(configDef)) {
                        throw new RuntimeException("Config definition could not be removed from registry");
                    }
                    return;
                }
            }
            throw new RuntimeException("No such config definition registered");
        } catch (Exception e) {
            throw new RuntimeException("Unregistering definition for config type \"" + configTypeUri + "\" failed", e);
        }
    }

    // --- Listeners ---

    @Override
    public void postCreateTopic(Topic topic) {
        for (ConfigDef configDef : getApplicableConfigDefs(topic)) {
            _createConfigTopic(configDef, topic);
        }
    }

    // ------------------------------------------------------------------------------------------------- Private Methods

    private RelatedTopic _getConfigTopic(String configTypeUri, long topicId) {
        return dmx.getPrivilegedAccess().getConfigTopic(configTypeUri, topicId);
    }

    private void _createConfigTopic(final ConfigDef configDef, final Topic topic) {
        final String configTypeUri = configDef.getConfigTypeUri();
        try {
            logger.info("Creating config topic of type \"" + configTypeUri + "\" for topic " + topic.getId());
            final PrivilegedAccess pa = dmx.getPrivilegedAccess();
            // Note: a config topic (and its assoc) require special workspace assignments.
            // Creating the assignments requires priviledged access. Consider e.g. the Username topic created while
            // 1st LDAP login: assigning the user's config topics to the "Administration" workspace would fail.
            // Only admins can do the assignments, but at this moment no user is logged in.
            RelatedTopic configTopic = pa.runInWorkspaceContext(-1, () -> {
                return createConfigAssoc(
                    topic,
                    dmx.createTopic(configDef.getConfigValue(topic))
                ).getDMXObjectByRole(DEFAULT);
            });
            long workspaceId = workspaceId(configDef.getConfigModRole());
            pa.assignToWorkspace(configTopic, workspaceId);
            pa.assignToWorkspace(configTopic.getRelatingAssoc(), workspaceId);
        } catch (Exception e) {
            throw new RuntimeException("Creating config topic of type \"" + configTypeUri + "\" for topic " +
                topic.getId() + " failed", e);
        }
    }

    private Assoc createConfigAssoc(Topic topic, Topic configTopic) {
        return dmx.createAssoc(mf.newAssocModel(CONFIGURATION,
            mf.newTopicPlayerModel(topic.getId(), CONFIGURABLE),
            mf.newTopicPlayerModel(configTopic.getId(), DEFAULT)
        ));
    }

    private long workspaceId(ConfigModRole role) {
        PrivilegedAccess pa = dmx.getPrivilegedAccess();
        switch (role) {
        case ADMIN:
            return pa.getAdminWorkspaceId();
        case SYSTEM:
            return pa.getSystemWorkspaceId();
        default:
            throw new RuntimeException("Modification role \"" + role + "\" not yet implemented");
        }
    }

    // ---

    /**
     * Returns all config definitions applicable to a given topic.
     *
     * @return  a list of config definitions, possibly empty.
     */
    private List getApplicableConfigDefs(Topic topic) {
        List configDefs1 = lookupConfigDefs(ConfigTarget.SINGLETON.hashKey(topic));
        List configDefs2 = lookupConfigDefs(ConfigTarget.TYPE_INSTANCES.hashKey(topic));
        if (configDefs1 != null && configDefs2 != null) {
            List configDefs = new ArrayList();
            configDefs.addAll(configDefs1);
            configDefs.addAll(configDefs2);
            return configDefs;
        }
        return configDefs1 != null ? configDefs1 : configDefs2 != null ? configDefs2 : new ArrayList();
    }

    /**
     * Returns the config definition for the given config type that is applicable to the given topic.
     *
     * @throws RuntimeException     if no such config definition is registered.
     */
    private ConfigDef getApplicableConfigDef(Topic topic, String configTypeUri) {
        List configDefs = getApplicableConfigDefs(topic);
        if (configDefs.size() == 0) {
            throw new RuntimeException("None of the registered config definitions are applicable to " + info(topic));
        }
        ConfigDef configDef = findByConfigTypeUri(configDefs, configTypeUri);
        if (configDef == null) {
            throw new RuntimeException("For " + info(topic) + " no config definition for type \"" + configTypeUri +
                "\" registered");
        }
        return configDef;
    }

    // ---

    private boolean isRegistered(ConfigDef configDef) {
        for (List configDefs : registry.values()) {
            if (configDefs.contains(configDef)) {
                return true;
            }
        }
        return false;
    }

    private ConfigDef findByConfigTypeUri(List configDefs, String configTypeUri) {
        for (ConfigDef configDef : configDefs) {
            if (configDef.getConfigTypeUri().equals(configTypeUri)) {
                return configDef;
            }
        }
        return null;
    }

    private List lookupConfigDefs(String hashKey) {
        return registry.get(hashKey);
    }

    // ---

    private String info(Topic topic) {
        return "topic " + topic.getId() + " (value=\"" + topic.getSimpleValue() + "\", typeUri=\"" +
            topic.getTypeUri() + "\", uri=\"" + topic.getUri() + "\")";
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy