package de.deepamehta.config;
import de.deepamehta.core.RelatedTopic;
import de.deepamehta.core.Topic;
import de.deepamehta.core.model.AssociationModel;
import de.deepamehta.core.model.SimpleValue;
import de.deepamehta.core.model.TopicRoleModel;
import de.deepamehta.core.osgi.PluginActivator;
import de.deepamehta.core.service.Transactional;
import de.deepamehta.core.service.accesscontrol.AccessControl;
import de.deepamehta.core.service.event.PostCreateTopicListener;
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.core.Context;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.logging.Logger;
@Path("/config")
@Produces("application/json")
public class ConfigPlugin extends PluginActivator implements ConfigService, PostCreateTopicListener {
// ------------------------------------------------------------------------------------------------------- Constants
private static String ASSOC_TYPE_CONFIGURATION = "dm4.config.configuration";
private static String ROLE_TYPE_CONFIGURABLE = "dm4.config.configurable";
private static String ROLE_TYPE_DEFAULT = "dm4.core.default";
// ---------------------------------------------------------------------------------------------- Instance Variables
/**
* Key: the "configurable URI" as a config target's hash key, that is either "topic_uri:{uri}" or "type_uri:{uri}".
*/
private Map<String, List<ConfigDefinition>> registry = new HashMap();
@Context
private HttpServletRequest request;
private Logger logger = Logger.getLogger(getClass().getName());
// -------------------------------------------------------------------------------------------------- Public Methods
// ************************************
// *** ConfigService Implementation ***
// ************************************
@GET
@Path("/{config_type_uri}/topic/{topic_id}")
@Override
public RelatedTopic getConfigTopic(@PathParam("config_type_uri") String configTypeUri,
@PathParam("topic_id") long topicId) {
return _getConfigTopic(configTypeUri, topicId);
}
@Override
public void createConfigTopic(String configTypeUri, Topic topic) {
_createConfigTopic(getApplicableConfigDefinition(topic, configTypeUri), topic);
}
// ---
@Override
public void registerConfigDefinition(ConfigDefinition configDef) {
try {
if (isRegistered(configDef)) {
throw new RuntimeException("A definition for config type \"" + configDef.getConfigTypeUri() +
"\" is already registered");
}
//
String hashKey = configDef.getHashKey();
List<ConfigDefinition> configDefs = lookupConfigDefinitions(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 unregisterConfigDefinition(String configTypeUri) {
try {
for (List<ConfigDefinition> configDefs : registry.values()) {
ConfigDefinition 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);
}
}
// --- not part of OSGi service ---
@GET
public ConfigDefinitions getConfigDefinitions() {
try {
JSONObject json = new JSONObject();
AccessControl ac = dm4.getAccessControl();
for (String configurableUri: registry.keySet()) {
JSONArray array = new JSONArray();
for (ConfigDefinition configDef : lookupConfigDefinitions(configurableUri)) {
String username = ac.getUsername(request);
long workspaceId = workspaceId(configDef.getConfigModificationRole());
if (ac.hasReadPermission(username, workspaceId)) {
array.put(configDef.getConfigTypeUri());
}
}
json.put(configurableUri, array);
}
return new ConfigDefinitions(json);
} catch (Exception e) {
throw new RuntimeException("Retrieving the registered config definitions failed", e);
}
}
// ********************************
// *** Listener Implementations ***
// ********************************
@Override
public void postCreateTopic(Topic topic) {
for (ConfigDefinition configDef : getApplicableConfigDefinitions(topic)) {
_createConfigTopic(configDef, topic);
}
}
// ------------------------------------------------------------------------------------------------- Private Methods
private RelatedTopic _getConfigTopic(String configTypeUri, long topicId) {
return dm4.getAccessControl().getConfigTopic(configTypeUri, topicId);
}
private RelatedTopic _createConfigTopic(final ConfigDefinition configDef, final Topic topic) {
final String configTypeUri = configDef.getConfigTypeUri();
try {
logger.info("### Creating config topic of type \"" + configTypeUri + "\" for topic " + topic.getId());
// suppress standard workspace assignment as a config topic requires a special assignment
final AccessControl ac = dm4.getAccessControl();
return ac.runWithoutWorkspaceAssignment(new Callable<RelatedTopic>() {
@Override
public RelatedTopic call() {
Topic configTopic = dm4.createTopic(configDef.getConfigValue(topic));
dm4.createAssociation(mf.newAssociationModel(ASSOC_TYPE_CONFIGURATION,
mf.newTopicRoleModel(topic.getId(), ROLE_TYPE_CONFIGURABLE),
mf.newTopicRoleModel(configTopic.getId(), ROLE_TYPE_DEFAULT)));
ac.assignToWorkspace(configTopic, workspaceId(configDef.getConfigModificationRole()));
// ### TODO: extend Core API to avoid re-retrieval
return _getConfigTopic(configTypeUri, topic.getId());
}
});
} catch (Exception e) {
throw new RuntimeException("Creating config topic of type \"" + configTypeUri + "\" for topic " +
topic.getId() + " failed", e);
}
}
private long workspaceId(ConfigModificationRole role) {
AccessControl ac = dm4.getAccessControl();
switch (role) {
case ADMIN:
return ac.getAdministrationWorkspaceId();
case SYSTEM:
return ac.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<ConfigDefinition> getApplicableConfigDefinitions(Topic topic) {
List<ConfigDefinition> configDefs1 = lookupConfigDefinitions(ConfigTarget.SINGLETON.hashKey(topic));
List<ConfigDefinition> configDefs2 = lookupConfigDefinitions(ConfigTarget.TYPE_INSTANCES.hashKey(topic));
if (configDefs1 != null && configDefs2 != null) {
List<ConfigDefinition> 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 ConfigDefinition getApplicableConfigDefinition(Topic topic, String configTypeUri) {
List<ConfigDefinition> configDefs = getApplicableConfigDefinitions(topic);
if (configDefs.size() == 0) {
throw new RuntimeException("None of the registered config definitions are applicable to " + info(topic));
}
ConfigDefinition 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(ConfigDefinition configDef) {
for (List<ConfigDefinition> configDefs : registry.values()) {
if (configDefs.contains(configDef)) {
return true;
}
}
return false;
}
private ConfigDefinition findByConfigTypeUri(List<ConfigDefinition> configDefs, String configTypeUri) {
for (ConfigDefinition configDef : configDefs) {
if (configDef.getConfigTypeUri().equals(configTypeUri)) {
return configDef;
}
}
return null;
}
private List<ConfigDefinition> lookupConfigDefinitions(String hashKey) {
return registry.get(hashKey);
}
// ---
private String info(Topic topic) {
return "topic " + topic.getId() + " (value=\"" + topic.getSimpleValue() + "\", typeUri=\"" +
topic.getTypeUri() + "\", uri=\"" + topic.getUri() + "\")";
}
}