Yair Zaslavsky has uploaded a new change for review.

Change subject: aaa: WIP -  New Extensions Manager based on the extensions API
......................................................................

aaa: WIP -  New Extensions Manager based on the extensions API

This is just the beginning of my work on the extensions manager

Change-Id: I7d170d5dda990fd85e9843ecbb4909749a88df75
Topic: AAA
Signed-off-by: Yair Zaslavsky <yzasl...@redhat.com>
---
M backend/manager/modules/extensions-api-root/pom.xml
A 
backend/manager/modules/extensions-manager/src/main/java/org/ovirt/engine/core/extensions/mgr/ExtensionsManagerNew.java
2 files changed, 332 insertions(+), 0 deletions(-)


  git pull ssh://gerrit.ovirt.org:29418/ovirt-engine refs/changes/27/26427/1

diff --git a/backend/manager/modules/extensions-api-root/pom.xml 
b/backend/manager/modules/extensions-api-root/pom.xml
index 179c78e..6d32d77 100644
--- a/backend/manager/modules/extensions-api-root/pom.xml
+++ b/backend/manager/modules/extensions-api-root/pom.xml
@@ -13,6 +13,8 @@
 
   <properties>
     <animal.sniffer.signature>java17</animal.sniffer.signature>
+    <maven.compiler.source>1.7</maven.compiler.source>
+    <maven.compiler.target>1.7</maven.compiler.target>
   </properties>
 
   <modules>
diff --git 
a/backend/manager/modules/extensions-manager/src/main/java/org/ovirt/engine/core/extensions/mgr/ExtensionsManagerNew.java
 
b/backend/manager/modules/extensions-manager/src/main/java/org/ovirt/engine/core/extensions/mgr/ExtensionsManagerNew.java
new file mode 100644
index 0000000..6a5d220
--- /dev/null
+++ 
b/backend/manager/modules/extensions-manager/src/main/java/org/ovirt/engine/core/extensions/mgr/ExtensionsManagerNew.java
@@ -0,0 +1,330 @@
+package org.ovirt.engine.core.extensions.mgr;
+
+import static java.util.Arrays.sort;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.ServiceLoader;
+
+import org.apache.commons.lang.math.RandomUtils;
+import org.jboss.modules.Module;
+import org.jboss.modules.ModuleIdentifier;
+import org.jboss.modules.ModuleLoadException;
+import org.jboss.modules.ModuleLoader;
+import org.ovirt.engine.api.extensions.Extension.ExtensionProperties;
+import org.ovirt.engine.core.utils.EngineLocalConfig;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import extensions.Base;
+import extensions.Base.ConfigKeys;
+import extensions.Base.ContextKeys;
+import extensions.ExtMap;
+import extensions.Extension;
+/**
+ * This class is responsible for loading the required {@code Configuration} in 
order to create an extension. It holds
+ * the logic of ordering and solving conflicts during loading the configuration
+ */
+public class ExtensionsManagerNew {
+    private static final String ENGINE_EXTENSION_ENABLED = 
"ENGINE_EXTENSION_ENABLED_";
+
+    public class ExtensionEntry {
+        private File file;
+        private boolean enabled;
+        private boolean activated;
+        private Extension extension;
+        private ExtMap context;
+        private Logger logger = log;
+
+        public ExtensionEntry(Properties props, File file) {
+            this.file = file;
+            load(props);
+        }
+
+        public String getName() {
+            return (String) context.get(ExtensionProperties.NAME);
+        }
+
+        public File getFile() {
+            return file;
+        }
+
+        public boolean isEnabled() {
+            return enabled;
+        }
+
+        public boolean isActivated() {
+            return activated;
+        }
+
+        public List<String> getProvides() {
+            List<String> providers = new ArrayList<>();
+            for (String provider : ((String) 
context.get(Base.ContextKeys.PROVIDES)).split(",")) {
+                providers.add(provider.trim());
+            }
+            return providers;
+        }
+
+        public ExtMap getContext() {
+            return context;
+        }
+
+        public Extension getExtension() {
+            return extension;
+        }
+
+        public Properties getConfig() {
+            return (Properties) context.get(ExtensionProperties.CONFIGURATION);
+        }
+
+        private void load(Properties props) {
+            enabled = props.get(ConfigKeys.ENABLED) != null ? 
Boolean.parseBoolean(props.getProperty(ConfigKeys.ENABLED)) : true;
+        }
+
+    }
+
+    private static final Logger log = 
LoggerFactory.getLogger(ExtensionsManager.class);
+    private static volatile ExtensionsManagerNew instance = null;
+    private Map<String, ExtensionEntry> loadedEntries = new HashMap<>();
+    private Map<String, Module> loadedModules = new HashMap<>();
+
+    public static ExtensionsManagerNew getInstance() {
+        if (instance == null) {
+            synchronized (ExtensionsManager.class) {
+                if (instance == null) {
+                    instance = new ExtensionsManagerNew();
+                }
+            }
+        }
+        return instance;
+    }
+
+    public List<ExtensionEntry> getProvidedExtensions(String provides) {
+        List<ExtensionEntry> results = new ArrayList<>();
+        for (ExtensionEntry entry : loadedEntries.values()) {
+            if (entry.activated && entry.getProvides().contains(provides)) {
+                results.add(entry);
+            }
+        }
+        return results;
+    }
+
+    public ExtensionEntry getExtensionByName(String pluginName) throws 
ConfigurationException {
+        ExtensionEntry result = loadedEntries.get(pluginName);
+        if (result == null) {
+            throw new ConfigurationException(String.format(
+                            "No configuration was found for extension named 
'%1$s'",
+                            pluginName)
+                    );
+
+        }
+        if (!result.activated) {
+            throw new ConfigurationException(String.format(
+                        "The configuration '%1$s' is not active",
+                        pluginName)
+                    );
+        }
+        return result;
+    }
+
+    private ExtensionsManagerNew() {
+        for (File directory : 
EngineLocalConfig.getInstance().getExtensionsDirectories()) {
+            if (!directory.exists()) {
+                log.warn(String.format("The directory '%1$s' cotaning 
configuration files does not exist.",
+                        directory.getAbsolutePath()));
+            } else {
+
+                // The order of the files inside the directory is relevant, as 
the objects are created in the same order
+                // that
+                // the files are processed, so it is better to sort them so 
that objects will always be created in the
+                // same
+                // order regardless of how the filesystem decides to store the 
entries of the directory:
+                File[] files = directory.listFiles();
+                if (files != null) {
+                    sort(files);
+                    for (File file : files) {
+                        if (file.getName().endsWith(".properties")) {
+                            load(file);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    public void load(Properties configuration) {
+        loadImpl(configuration, null);
+    }
+
+    public void load(File file) {
+        try (FileInputStream inputStream = new FileInputStream(file)) {
+            Properties props = new Properties();
+            props.load(inputStream);
+            loadImpl(props, file);
+        } catch (IOException exception) {
+            throw new ConfigurationException(String.format("Can't load object 
configuration file '%1$s'",
+                    file.getAbsolutePath()));
+        }
+    }
+
+    private synchronized void loadImpl(Properties props, File confFile) {
+        ExtensionEntry entry = new ExtensionEntry(props, confFile);
+        ExtensionEntry alreadyLoadedEntry = loadedEntries.get(entry.getName());
+        if (alreadyLoadedEntry != null) {
+            throw new ConfigurationException(String.format(
+                    "Could not load the configuration '%1$s' from file %2$s. A 
configuration with the same name was already loaded from file %3$s",
+                    entry.getName(),
+                    getFileName(entry.file),
+                    getFileName(alreadyLoadedEntry.file))
+             );
+        }
+        loadedEntries.put(entry.getName(), entry);
+                
EngineLocalConfig.getInstance().getBoolean(ENGINE_EXTENSION_ENABLED + 
entry.getName(), entry.enabled);
+        //Activate the extension
+        if (entry.enabled && entry.extension == null) {
+            try {
+                entry.context = new ExtMap();
+                entry.context.put(ContextKeys.CONFIGURATION, props);
+                entry.context.put(ContextKeys.INSTANCE_NAME, 
props.getProperty(ConfigKeys.NAME) != null ? props.getProperty(ConfigKeys.NAME) 
: String.format("INSTANCE_NAME_%1$s", RandomUtils.nextInt()));
+                entry.context.put(ContextKeys.PROVIDES, 
props.getProperty(ConfigKeys.PROVIDES) != null ? 
props.getProperty(ConfigKeys.PROVIDES) : "");
+
+                entry.extension = (Extension) lookupService(
+                        Extension.class,
+                        entry.getConfig().getProperty(ConfigKeys.CLASS),
+                        entry.getConfig().getProperty(ConfigKeys.MODULE)
+                        ).newInstance();
+
+                ExtMap input = new ExtMap().mput(
+                        Base.InvokeKeys.COMMAND,
+                        Base.InvokeCommands.INITIALIZE
+                ).mput(
+                        Base.InvokeKeys.CONTEXT,
+                        entry.context
+                      );
+                ExtMap output = new ExtMap();
+                dumpMap(entry, input);
+                try {
+                    entry.extension.invoke(input, output);
+                } catch (Exception ex) {
+                    output.mput(
+                            Base.InvokeKeys.RESULT,
+                            Base.InvokeResult.FAILED
+                            ).mput(
+                                    Base.InvokeKeys.MESSAGE,
+                                    ex.getMessage()
+                            );
+                }
+                entry.logger =
+                        LoggerFactory.getLogger(String.format(
+                                "%1$s.dump.%2$s.%3%s",
+                                ExtensionsManagerNew.class.getName(),
+                                
entry.context.get(Base.ContextKeys.EXTENSION_NAME),
+                                
entry.context.get(Base.ContextKeys.INSTANCE_NAME)
+                                )
+                        );
+                dumpMap(entry, output);
+                int result = output.<Integer> get(Base.InvokeKeys.RESULT);
+                switch (result) {
+                case Base.InvokeResult.SUCCESS:
+                    break;
+                case Base.InvokeResult.FAILED:
+                    throw new RuntimeException(output.<String> 
get(Base.InvokeKeys.MESSAGE));
+                }
+
+                entry.activated = true;
+
+            } catch (Exception ex) {
+                log.error(
+                        String.format(
+                                "Error in activating extension %1$s. Exception 
message is %2$s",
+                                entry.getName(),
+                                ex.getMessage()
+                                )
+                        );
+                if (log.isDebugEnabled()) {
+                    log.error("", ex);
+                }
+            }
+        }
+    }
+
+    private String getFileName(File file) {
+        return file != null ? file.getAbsolutePath() : "N/A";
+    }
+
+    private Module loadModule(String moduleSpec) {
+        // If the module was not already loaded, load it
+        try {
+            Module module = loadedModules.get(moduleSpec);
+            if (module == null) {
+                ModuleLoader loader = ModuleLoader.forClass(this.getClass());
+                if (loader == null) {
+                    throw new ConfigurationException(String.format("The module 
'%1$s' cannot be loaded as the module system isn't enabled.",
+                            moduleSpec));
+                }
+                module = 
loader.loadModule(ModuleIdentifier.fromString(moduleSpec));
+                loadedModules.put(moduleSpec, module);
+            }
+            return module;
+        } catch (ModuleLoadException exception) {
+            throw new ConfigurationException(String.format("The module '%1$s' 
cannot be loaded.", moduleSpec),
+                    exception);
+        }
+    }
+
+    private Class<?> lookupService(Class<?> serviceInterface, String 
serviceClassName, String moduleName) {
+        // Iterate over the service classes, and find the one that should
+            // be instantiated and initialized.
+        Module module = loadModule(moduleName);
+        Class<?> serviceClass = null;
+        for (Object service : ServiceLoader.load(serviceInterface, 
module.getClassLoader())) {
+            if (service.getClass().getName().equals(serviceClassName)) {
+                serviceClass = service.getClass();
+                break;
+            }
+        }
+        if (serviceClass == null) {
+            throw new ConfigurationException(String.format("The module '%1$s' 
does not contain the service '%2$s'.",
+                    module.getIdentifier().getName(),
+                    serviceClassName));
+        }
+        return serviceClass;
+    }
+
+    private void dumpMap(ExtensionEntry entry, ExtMap map) {
+        if (entry.logger.isDebugEnabled()) {
+            entry.logger.debug(map.toString());
+        }
+    }
+
+    public void dump() {
+        log.info("Start of enabled extensions list");
+        for (ExtensionEntry entry : loadedEntries.values()) {
+            if (entry.extension != null) {
+                log.info(String.format(
+                        "Instance name: '%1$s', Extension name: '%2$s', 
Version: '%3$s', License: '%4$s', Home: '%5$s', Author '%6$s',  File: '%7$s', 
Activated: '%8$s",
+                        
emptyIfNull(entry.context.get(ContextKeys.INSTANCE_NAME)),
+                        
emptyIfNull(entry.context.get(ContextKeys.EXTENSION_NAME)),
+                        emptyIfNull(entry.context.get(ContextKeys.VERSION)),
+                        emptyIfNull(entry.context.get(ContextKeys.LICENSE)),
+                        emptyIfNull(entry.context.get(ContextKeys.HOME_URL)),
+                        emptyIfNull(entry.context.get(ContextKeys.AUTHOR)),
+                        emptyIfNull(getFileName(entry.file)),
+                        entry.activated
+                        )
+                        );
+            }
+        }
+        log.info("End of enabled extensions list");
+    }
+
+    private Object emptyIfNull(Object value) {
+        return value == null ? "" : value;
+    }
+}


-- 
To view, visit http://gerrit.ovirt.org/26427
To unsubscribe, visit http://gerrit.ovirt.org/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I7d170d5dda990fd85e9843ecbb4909749a88df75
Gerrit-PatchSet: 1
Gerrit-Project: ovirt-engine
Gerrit-Branch: master
Gerrit-Owner: Yair Zaslavsky <yzasl...@redhat.com>
_______________________________________________
Engine-patches mailing list
Engine-patches@ovirt.org
http://lists.ovirt.org/mailman/listinfo/engine-patches

Reply via email to