From f12f541b11a46934d7db537eb5a3166749f5c4f3 Mon Sep 17 00:00:00 2001 From: Luca Frosini Date: Thu, 16 Jun 2022 13:54:13 +0200 Subject: [PATCH] Added inital code --- FUNDING.md | 26 + .../handler/resourceregistry/Constants.java | 28 + .../resourceregistry/ContextUtility.java | 87 +++ .../resourceregistry/EServiceHandler.java | 275 ++++++++ .../resourceregistry/HostingNodeHandler.java | 263 +++++++ .../resourcemanager/EServiceManager.java | 410 +++++++++++ .../resourcemanager/HostingNodeManager.java | 659 ++++++++++++++++++ .../LinuxDistributionInfo.java | 93 +++ 8 files changed, 1841 insertions(+) create mode 100644 FUNDING.md create mode 100644 src/main/java/org/gcube/smartgears/handler/resourceregistry/Constants.java create mode 100644 src/main/java/org/gcube/smartgears/handler/resourceregistry/ContextUtility.java create mode 100644 src/main/java/org/gcube/smartgears/handler/resourceregistry/EServiceHandler.java create mode 100644 src/main/java/org/gcube/smartgears/handler/resourceregistry/HostingNodeHandler.java create mode 100644 src/main/java/org/gcube/smartgears/handler/resourceregistry/resourcemanager/EServiceManager.java create mode 100644 src/main/java/org/gcube/smartgears/handler/resourceregistry/resourcemanager/HostingNodeManager.java create mode 100644 src/main/java/org/gcube/smartgears/handler/resourceregistry/resourcemanager/LinuxDistributionInfo.java diff --git a/FUNDING.md b/FUNDING.md new file mode 100644 index 0000000..9e48b94 --- /dev/null +++ b/FUNDING.md @@ -0,0 +1,26 @@ +# Acknowledgments + +The projects leading to this software have received funding from a series of European Union programmes including: + +- the Sixth Framework Programme for Research and Technological Development + - [DILIGENT](https://cordis.europa.eu/project/id/004260) (grant no. 004260). +- the Seventh Framework Programme for research, technological development and demonstration + - [D4Science](https://cordis.europa.eu/project/id/212488) (grant no. 212488); + - [D4Science-II](https://cordis.europa.eu/project/id/239019) (grant no.239019); + - [ENVRI](https://cordis.europa.eu/project/id/283465) (grant no. 283465); + - [iMarine](https://cordis.europa.eu/project/id/283644) (grant no. 283644); + - [EUBrazilOpenBio](https://cordis.europa.eu/project/id/288754) (grant no. 288754). +- the H2020 research and innovation programme + - [SoBigData](https://cordis.europa.eu/project/id/654024) (grant no. 654024); + - [PARTHENOS](https://cordis.europa.eu/project/id/654119) (grant no. 654119); + - [EGI-Engage](https://cordis.europa.eu/project/id/654142) (grant no. 654142); + - [ENVRI PLUS](https://cordis.europa.eu/project/id/654182) (grant no. 654182); + - [BlueBRIDGE](https://cordis.europa.eu/project/id/675680) (grant no. 675680); + - [PerformFISH](https://cordis.europa.eu/project/id/727610) (grant no. 727610); + - [AGINFRA PLUS](https://cordis.europa.eu/project/id/731001) (grant no. 731001); + - [DESIRA](https://cordis.europa.eu/project/id/818194) (grant no. 818194); + - [ARIADNEplus](https://cordis.europa.eu/project/id/823914) (grant no. 823914); + - [RISIS 2](https://cordis.europa.eu/project/id/824091) (grant no. 824091); + - [EOSC-Pillar](https://cordis.europa.eu/project/id/857650) (grant no. 857650); + - [Blue Cloud](https://cordis.europa.eu/project/id/862409) (grant no. 862409); + - [SoBigData-PlusPlus](https://cordis.europa.eu/project/id/871042) (grant no. 871042); diff --git a/src/main/java/org/gcube/smartgears/handler/resourceregistry/Constants.java b/src/main/java/org/gcube/smartgears/handler/resourceregistry/Constants.java new file mode 100644 index 0000000..57a4920 --- /dev/null +++ b/src/main/java/org/gcube/smartgears/handler/resourceregistry/Constants.java @@ -0,0 +1,28 @@ +package org.gcube.smartgears.handler.resourceregistry; + +import org.gcube.smartgears.handler.resourceregistry.resourcemanager.HostingNodeManager; + +/** + * Library-wide constants. + * @author Luca Frosini + * @author Lucio Lelii + */ +public class Constants { + + public static final String HOSTING_NODE_MANAGER_PROPERTY = HostingNodeManager.class.getSimpleName(); + +// /** +// * The name of the context property that contains the EService Resource. +// */ +// public static final String ESERVICE_MANAGER_PROPERTY = EServiceManager.class.getSimpleName(); + + /** + * The configuration name of {@link EServiceHandler} and {@link HostingNodeHandler}. + */ + public static final String RESOURCE_MANAGEMENT = "resource-management"; + + public static final long default_container_publication_frequency_in_seconds = 60; + + public static final int application_republish_frequency_in_minutes = 20; + +} diff --git a/src/main/java/org/gcube/smartgears/handler/resourceregistry/ContextUtility.java b/src/main/java/org/gcube/smartgears/handler/resourceregistry/ContextUtility.java new file mode 100644 index 0000000..500ea0d --- /dev/null +++ b/src/main/java/org/gcube/smartgears/handler/resourceregistry/ContextUtility.java @@ -0,0 +1,87 @@ +package org.gcube.smartgears.handler.resourceregistry; + +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; +import java.util.UUID; + +import org.gcube.common.authorization.client.proxy.AuthorizationProxy; +import org.gcube.common.authorization.library.provider.SecurityTokenProvider; +import org.gcube.common.scope.api.ScopeProvider; +import org.gcube.informationsystem.resourceregistry.api.contexts.ContextCache; +import org.gcube.informationsystem.resourceregistry.api.exceptions.ResourceRegistryException; +import org.gcube.smartgears.provider.ProviderFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Luca Frosini (ISTI-CNR) + */ +public class ContextUtility { + + private static Logger logger = LoggerFactory.getLogger(ContextUtility.class); + + private static AuthorizationProxy authorizationProxy; + + static { + authorizationProxy = ProviderFactory.provider().authorizationProxy(); + } + + public static void resetContex() { + SecurityTokenProvider.instance.reset(); + ScopeProvider.instance.reset(); + } + + public static void setContextFromToken(String token) { + if (token == null || token.compareTo("") == 0) { + resetContex(); + } else { + SecurityTokenProvider.instance.set(token); + String scope = getContextName(token); + ScopeProvider.instance.set(scope); + } + + } + + public static String getCurrentContextName() { + String token = SecurityTokenProvider.instance.get(); + return getContextName(token); + } + + public static UUID getContextUUID(String token) throws ResourceRegistryException { + ContextCache contextCache = ContextCache.getInstance(); + String contextFullName = getContextName(token); + UUID contextUUID = contextCache.getUUIDByFullName(contextFullName); + return contextUUID; + } + + public static String getContextName(String token) { + try { + return authorizationProxy.get(token).getContext(); + } catch (Exception e) { + logger.error("Error retrieving context form token {}, it should never happen", token, e); + return null; + } + } + + public static SortedSet getContextFullNamesFromTokens(Set tokens){ + SortedSet contextFullNames = new TreeSet<>(); + for(String token : tokens) { + String contextFullName = getContextName(token); + contextFullNames.add(contextFullName); + } + return contextFullNames; + } + + public static SortedSet getContextUUIDFromTokens(Set tokens) throws ResourceRegistryException { + SortedSet contextsUUID = new TreeSet<>(); + ContextCache contextCache = ContextCache.getInstance(); + for(String token : tokens) { + String contextFullName = getContextName(token); + UUID contextUUID = contextCache.getUUIDByFullName(contextFullName); + contextsUUID.add(contextUUID); + } + return contextsUUID; + } + +} diff --git a/src/main/java/org/gcube/smartgears/handler/resourceregistry/EServiceHandler.java b/src/main/java/org/gcube/smartgears/handler/resourceregistry/EServiceHandler.java new file mode 100644 index 0000000..a84e7ea --- /dev/null +++ b/src/main/java/org/gcube/smartgears/handler/resourceregistry/EServiceHandler.java @@ -0,0 +1,275 @@ +package org.gcube.smartgears.handler.resourceregistry; + +import static org.gcube.common.events.Observes.Kind.resilient; +import static org.gcube.smartgears.handlers.ProfileEvents.addToContext; +import static org.gcube.smartgears.handlers.ProfileEvents.removeFromContext; +import static org.gcube.smartgears.lifecycle.application.ApplicationLifecycle.activation; +import static org.gcube.smartgears.lifecycle.application.ApplicationLifecycle.failure; +import static org.gcube.smartgears.lifecycle.application.ApplicationLifecycle.stop; +import static org.gcube.smartgears.utils.Utils.rethrowUnchecked; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +import org.gcube.common.authorization.library.provider.SecurityTokenProvider; +import org.gcube.common.events.Observes; +import org.gcube.informationsystem.model.reference.entities.Resource; +import org.gcube.informationsystem.model.reference.relations.ConsistsOf; +import org.gcube.informationsystem.resourceregistry.api.contexts.ContextCache; +import org.gcube.resourcemanagement.model.reference.entities.facets.StateFacet; +import org.gcube.resourcemanagement.model.reference.entities.resources.EService; +import org.gcube.smartgears.context.application.ApplicationContext; +import org.gcube.smartgears.handler.resourceregistry.resourcemanager.EServiceManager; +import org.gcube.smartgears.handlers.application.ApplicationLifecycleEvent.Start; +import org.gcube.smartgears.lifecycle.application.ApplicationLifecycle; +import org.gcube.smartgears.lifecycle.application.ApplicationState; +import org.gcube.smartgears.lifecycle.container.ContainerLifecycle; +import org.gcube.smartgears.utils.Utils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Manages the {@link EService} {@link Resource} of the application. + *

+ * The manager: + *

    + *
  • creates the {@link EService} {@link Resource} and the facets it + * {@link ConsistsOf} when the application starts for the first time;
  • + *
  • update the {@link StateFacet} when the application becomes active, + * and at any lifecycle change thereafter;
  • + *
+ *

+ * + * @author Luca Frosini (ISTI-CNR) + */ +public class EServiceHandler { + + private static final Logger logger = LoggerFactory.getLogger(EServiceHandler.class); + + private ApplicationContext applicationContext; + private ScheduledFuture periodicUpdates; + + protected EServiceManager eServiceManager; + + public EServiceHandler() { + super(); + } + + public void onStart(Start event) { + try { + logger.info("{} onStart started", this.getClass().getSimpleName()); + this.applicationContext = event.context(); + init(); + registerObservers(); + schedulePeriodicUpdates(); + logger.info("{} onStart terminated", this.getClass().getSimpleName()); + } catch (Throwable re) { + logger.error("onStart failed", re); + } + } + + protected void removeResourceFromOldContexts(Set startContexts, Set resourceContexts) { + Set contextsToRemove = new HashSet<>(resourceContexts); + contextsToRemove.removeAll(startContexts); + for(UUID contextToRemove : contextsToRemove) { + try { + eServiceManager.removeFromContext(contextToRemove); + }catch (Exception e) { + try { + String contextFullName = ContextCache.getInstance().getContextFullNameByUUID(contextToRemove); + logger.warn("Unable to remove {} from Context {} UUID {}", EService.NAME, contextFullName, contextsToRemove, e); + }catch (Exception ex) { + logger.warn("Unable to remove {} from Context with UUID {}.", EService.NAME, contextsToRemove, e); + } + } + } + } + + private void init() { + ClassLoader contextCL = Thread.currentThread().getContextClassLoader(); + String previousToken = SecurityTokenProvider.instance.get(); + try { + Thread.currentThread().setContextClassLoader(EServiceHandler.class.getClassLoader()); + boolean create = true; + + Set startTokens = applicationContext.configuration().startTokens(); + + String firstToken = startTokens.iterator().next(); + ContextUtility.setContextFromToken(firstToken); + eServiceManager = new EServiceManager(applicationContext); + + + Set startContextsUUID = new HashSet<>(); + for (String token : startTokens) { + UUID contextUUID = ContextUtility.getContextUUID(token); + startContextsUUID.add(contextUUID); + if (create) { + eServiceManager.createEService(); + create = false; + } else { + eServiceManager.addToContext(contextUUID); + } + } + + Set resourceContextsUUID = eServiceManager.getContextsUUID().keySet(); + removeResourceFromOldContexts(startContextsUUID, resourceContextsUUID); + + } catch (Throwable e) { + rethrowUnchecked(e); + } finally { + ContextUtility.setContextFromToken(previousToken); + Thread.currentThread().setContextClassLoader(contextCL); + } + logger.info("{} init() terminated", this.getClass().getSimpleName()); + } + + private void registerObservers() { + + applicationContext.events().subscribe(new Object() { + + @Observes({ activation, stop, failure }) + void onChanged(ApplicationLifecycle lc) { + ClassLoader contextCL = Thread.currentThread().getContextClassLoader(); + String previousToken = SecurityTokenProvider.instance.get(); + try { + Thread.currentThread().setContextClassLoader(EServiceHandler.class.getClassLoader()); + if(previousToken==null) { + String token = applicationContext.configuration().startTokens().iterator().next(); + ContextUtility.setContextFromToken(token); + } + eServiceManager.updateFacets(); + } catch (Exception e) { + logger.error("Failed to update {} State", EService.NAME, e); + } finally { + ContextUtility.setContextFromToken(previousToken); + Thread.currentThread().setContextClassLoader(contextCL); + } + } + + @Observes(value = addToContext) + void addTo(String token) { + ClassLoader contextCL = Thread.currentThread().getContextClassLoader(); + String previousToken = SecurityTokenProvider.instance.get(); + try { + Thread.currentThread().setContextClassLoader(EServiceHandler.class.getClassLoader()); + ContextUtility.setContextFromToken(token); + UUID contextUUID = ContextUtility.getContextUUID(token); + eServiceManager.addToContext(contextUUID); + } catch (Exception e) { + logger.error("Failed to add {} to current context ({})", EService.NAME, + ContextUtility.getCurrentContextName(), e); + } finally { + ContextUtility.setContextFromToken(previousToken); + Thread.currentThread().setContextClassLoader(contextCL); + } + } + + @Observes(value = removeFromContext) + void removeFrom(String token) { + ClassLoader contextCL = Thread.currentThread().getContextClassLoader(); + String previousToken = SecurityTokenProvider.instance.get(); + try { + Thread.currentThread().setContextClassLoader(EServiceHandler.class.getClassLoader()); + eServiceManager.removeFromCurrentContext(); + } catch (Exception e) { + logger.error("Failed to remove {} from current context ({})", + EService.NAME, ContextUtility.getCurrentContextName(), e); + } finally { + ContextUtility.setContextFromToken(previousToken); + Thread.currentThread().setContextClassLoader(contextCL); + } + + } + + }); + } + + private void schedulePeriodicUpdates() { + + // register to cancel updates + applicationContext.events().subscribe( + + new Object() { + + // we register it in response to lifecycle events so that we can + // stop and resume along with application + @Observes(value = { activation }, kind = resilient) + synchronized void restartPeriodicUpdates(final ApplicationLifecycle lc) { + + // already running + if (periodicUpdates != null) { + return; + } + + String applicationName = applicationContext.name(); + + if (lc.state() == ApplicationState.active) { + logger.info("Scheduling periodic updates of {} for application {}", + EService.NAME, applicationName); + } else { + logger.info("Resuming periodic updates of {} for application {}", + EService.NAME, applicationName); + } + + final Runnable updateTask = new Runnable() { + public void run() { + String previousToken = SecurityTokenProvider.instance.get(); + if (previousToken == null) { + String token = applicationContext.configuration().startTokens().iterator().next(); + ContextUtility.setContextFromToken(token); + } + try { + eServiceManager.updateFacets(); + } catch (Exception e) { + logger.error("Cannot complete periodic update of {} for application {}", EService.NAME, applicationName, e); + } finally { + ContextUtility.setContextFromToken(previousToken); + } + } + }; + + periodicUpdates = Utils.scheduledServicePool.scheduleAtFixedRate(updateTask, + Constants.application_republish_frequency_in_minutes, + Constants.application_republish_frequency_in_minutes, TimeUnit.MINUTES); + + /* + * The following line is used for testing purposes during development. + * If you uncomment this, you need to comment the line above + */ +// periodicUpdates = Utils.scheduledServicePool.scheduleAtFixedRate(updateTask, 120, 120, TimeUnit.SECONDS); + + } + + @Observes(value = { stop, failure }, kind = resilient) + synchronized void cancelPeriodicUpdates(ContainerLifecycle ignore) { + + if (periodicUpdates != null) { + + String applicationName = applicationContext.name(); + + logger.trace("Stopping periodic updates of {} for application {} ", + EService.NAME, applicationName); + + try { + periodicUpdates.cancel(true); + periodicUpdates = null; + } catch (Exception e) { + logger.warn("Could not stop periodic updates of {} for application {}", + EService.NAME, applicationName, e); + } + } + } + + }); + + } + + @Override + public String toString() { + return Constants.RESOURCE_MANAGEMENT; + } + +} diff --git a/src/main/java/org/gcube/smartgears/handler/resourceregistry/HostingNodeHandler.java b/src/main/java/org/gcube/smartgears/handler/resourceregistry/HostingNodeHandler.java new file mode 100644 index 0000000..1dd0893 --- /dev/null +++ b/src/main/java/org/gcube/smartgears/handler/resourceregistry/HostingNodeHandler.java @@ -0,0 +1,263 @@ +package org.gcube.smartgears.handler.resourceregistry; + +import static java.util.concurrent.TimeUnit.SECONDS; +import static org.gcube.common.events.Observes.Kind.resilient; +import static org.gcube.smartgears.handlers.ProfileEvents.addToContext; +import static org.gcube.smartgears.handlers.ProfileEvents.removeFromContext; +import static org.gcube.smartgears.lifecycle.container.ContainerLifecycle.activation; +import static org.gcube.smartgears.lifecycle.container.ContainerLifecycle.failure; +import static org.gcube.smartgears.lifecycle.container.ContainerLifecycle.part_activation; +import static org.gcube.smartgears.lifecycle.container.ContainerLifecycle.shutdown; +import static org.gcube.smartgears.lifecycle.container.ContainerLifecycle.stop; +import static org.gcube.smartgears.lifecycle.container.ContainerState.active; +import static org.gcube.smartgears.utils.Utils.rethrowUnchecked; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; + +import javax.xml.bind.annotation.XmlRootElement; + +import org.gcube.common.authorization.library.provider.SecurityTokenProvider; +import org.gcube.common.events.Observes; +import org.gcube.informationsystem.model.reference.entities.Facet; +import org.gcube.informationsystem.model.reference.entities.Resource; +import org.gcube.informationsystem.model.reference.relations.ConsistsOf; +import org.gcube.informationsystem.resourceregistry.api.contexts.ContextCache; +import org.gcube.informationsystem.resourceregistry.api.exceptions.ResourceRegistryException; +import org.gcube.resourcemanagement.model.reference.entities.facets.StateFacet; +import org.gcube.resourcemanagement.model.reference.entities.resources.HostingNode; +import org.gcube.smartgears.context.Property; +import org.gcube.smartgears.context.container.ContainerContext; +import org.gcube.smartgears.handler.resourceregistry.resourcemanager.HostingNodeManager; +import org.gcube.smartgears.handlers.container.ContainerHandler; +import org.gcube.smartgears.handlers.container.ContainerLifecycleEvent.Start; +import org.gcube.smartgears.lifecycle.container.ContainerLifecycle; +import org.gcube.smartgears.lifecycle.container.ContainerState; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Manages the {@link HostingNode} {@link Resource} of the application. + *

+ * The manager: + *

    + *
  • creates the {@link HostingNode} {@link Resource} and the facets it + * {@link ConsistsOf} when the container starts for the first time;
  • + *
  • update the {@link StateFacet} when the application becomes + * active, and at any lifecycle change thereafter;
  • + *
  • schedule a periodic update of {@link Facet}s containing variables + * information.
  • + *
+ *

+ * + * @author Luca Frosini (ISTI-CNR) + */ +public class HostingNodeHandler { + + private static Logger logger = LoggerFactory.getLogger(HostingNodeHandler.class); + + private ContainerContext containerContext; + private ScheduledFuture periodicUpdates; + + protected HostingNodeManager hostingNodeManager; + + public HostingNodeHandler() { + super(); + + } + + public void onStart(Start event) { + try { + logger.info("{} onStart started", this.getClass().getSimpleName()); + this.containerContext = event.context(); + init(); + registerObservers(); + schedulePeriodicUpdates(); + logger.info("{} onStart terminated", this.getClass().getSimpleName()); + } catch (Throwable re) { + logger.error("onStart failed", re); + } + } + + protected void removeResourceFromOldContexts(Set startContexts, Set resourceContexts) throws ResourceRegistryException { + Set contextsToRemove = new HashSet<>(resourceContexts); + contextsToRemove.removeAll(startContexts); + for(UUID contextToRemove : contextsToRemove) { + hostingNodeManager.removeFromContext(contextToRemove); + } + } + + private void init() { + ClassLoader contextCL = Thread.currentThread().getContextClassLoader(); + String previousToken = SecurityTokenProvider.instance.get(); + try { + Thread.currentThread().setContextClassLoader(HostingNodeHandler.class.getClassLoader()); + boolean create = true; + + List startTokens = containerContext.configuration().startTokens(); + + String firstToken = startTokens.iterator().next(); + ContextUtility.setContextFromToken(firstToken); + hostingNodeManager = new HostingNodeManager(containerContext); + + Set startContextsUUID = new HashSet<>(); + for (String token : startTokens) { + UUID contextUUID = ContextUtility.getContextUUID(token); + startContextsUUID.add(contextUUID); + if (create) { + hostingNodeManager.createHostingNode(); + containerContext.properties().add(new Property(Constants.HOSTING_NODE_MANAGER_PROPERTY, hostingNodeManager)); + create = false; + } else { + hostingNodeManager.addToContext(contextUUID); + } + } + + Set resourceContextsUUID = hostingNodeManager.getContextsUUID().keySet(); + removeResourceFromOldContexts(startContextsUUID, resourceContextsUUID); + + } catch (Throwable e) { + rethrowUnchecked(e); + } finally { + ContextUtility.setContextFromToken(previousToken); + Thread.currentThread().setContextClassLoader(contextCL); + } + logger.info("{} init() terminated", this.getClass().getSimpleName()); + } + + private void registerObservers() { + + containerContext.events().subscribe(new Object() { + + @Observes({ activation, part_activation, shutdown, stop, failure }) + void onChanged(ContainerLifecycle cl) { + ClassLoader contextCL = Thread.currentThread().getContextClassLoader(); + String previousToken = SecurityTokenProvider.instance.get(); + try { + Thread.currentThread().setContextClassLoader(HostingNodeHandler.class.getClassLoader()); + if (previousToken == null) { + String token = containerContext.configuration().startTokens().iterator().next(); + ContextUtility.setContextFromToken(token); + } + hostingNodeManager.updateFacets(); + } catch (Exception e) { + logger.error("Failed to update {} State", HostingNode.NAME, e); + } finally { + ContextUtility.setContextFromToken(previousToken); + Thread.currentThread().setContextClassLoader(contextCL); + } + } + + @Observes(value = addToContext) + void addTo(String token) { + ClassLoader contextCL = Thread.currentThread().getContextClassLoader(); + String previousToken = SecurityTokenProvider.instance.get(); + try { + Thread.currentThread().setContextClassLoader(HostingNodeHandler.class.getClassLoader()); + ContextUtility.setContextFromToken(token); + UUID contextUUID = ContextUtility.getContextUUID(token); + hostingNodeManager.addToContext(contextUUID); + } catch (Exception e) { + logger.error("Failed to update Service State", e); + } finally { + ContextUtility.setContextFromToken(previousToken); + Thread.currentThread().setContextClassLoader(contextCL); + } + } + + @Observes(value = removeFromContext) + void removeFrom(String token) { + ClassLoader contextCL = Thread.currentThread().getContextClassLoader(); + String previousToken = SecurityTokenProvider.instance.get(); + try { + Thread.currentThread().setContextClassLoader(HostingNodeHandler.class.getClassLoader()); + ContextUtility.setContextFromToken(token); + UUID contextUUID = ContextUtility.getContextUUID(token); + hostingNodeManager.removeFromContext(contextUUID); + } catch (Exception e) { + logger.error("Failed to update Service State", e); + } finally { + ContextUtility.setContextFromToken(previousToken); + Thread.currentThread().setContextClassLoader(contextCL); + } + } + + }); + } + + private void schedulePeriodicUpdates() { + // register to cancel updates + containerContext.events().subscribe( + + new Object() { + + final ScheduledExecutorService service = Executors.newScheduledThreadPool(1); + + // we register it in response to lifecycle events so that we can + // stop and resume along with application + @Observes(value = { activation, part_activation }, kind = resilient) + synchronized void restartPeriodicUpdates(ContainerLifecycle cl) { + + // already running + if (periodicUpdates != null) { + return; + } + + if (cl.state() == active) { + logger.info("Scheduling periodic updates of {}", HostingNode.NAME); + } else { + logger.info("Resuming periodic updates of {}", HostingNode.NAME); + } + + final Runnable updateTask = new Runnable() { + public void run() { + String previousToken = SecurityTokenProvider.instance.get(); + if (previousToken == null) { + String token = containerContext.configuration().startTokens().iterator().next(); + ContextUtility.setContextFromToken(token); + } + try { + hostingNodeManager.updateFacets(); + } catch (Exception e) { + logger.error("Cannot complete periodic update of {}", HostingNode.NAME, e); + }finally { + ContextUtility.setContextFromToken(previousToken); + } + } + }; + + periodicUpdates = service.scheduleAtFixedRate(updateTask, 3, + containerContext.configuration().publicationFrequency(), SECONDS); + + } + + @Observes(value = { stop, failure, shutdown }, kind = resilient) + synchronized void cancelPeriodicUpdates(ContainerLifecycle cl) { + + if (periodicUpdates != null) { + logger.trace("Stopping periodic updates of {}", HostingNode.NAME); + try { + periodicUpdates.cancel(true); + service.shutdownNow(); + periodicUpdates = null; + } catch (Exception e) { + logger.warn("Could not stop periodic updates of {}", HostingNode.NAME, e); + } + } + } + + }); + + } + + @Override + public String toString() { + return Constants.RESOURCE_MANAGEMENT; + } + +} diff --git a/src/main/java/org/gcube/smartgears/handler/resourceregistry/resourcemanager/EServiceManager.java b/src/main/java/org/gcube/smartgears/handler/resourceregistry/resourcemanager/EServiceManager.java new file mode 100644 index 0000000..0e45143 --- /dev/null +++ b/src/main/java/org/gcube/smartgears/handler/resourceregistry/resourcemanager/EServiceManager.java @@ -0,0 +1,410 @@ +package org.gcube.smartgears.handler.resourceregistry.resourcemanager; + +import java.net.URI; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; + +import javax.servlet.ServletRegistration; + +import org.gcube.common.authorization.library.provider.SecurityTokenProvider; +import org.gcube.informationsystem.model.impl.properties.HeaderImpl; +import org.gcube.informationsystem.model.impl.properties.PropagationConstraintImpl; +import org.gcube.informationsystem.model.reference.entities.Facet; +import org.gcube.informationsystem.model.reference.entities.Resource; +import org.gcube.informationsystem.model.reference.properties.Header; +import org.gcube.informationsystem.model.reference.properties.PropagationConstraint; +import org.gcube.informationsystem.model.reference.properties.PropagationConstraint.AddConstraint; +import org.gcube.informationsystem.model.reference.properties.PropagationConstraint.RemoveConstraint; +import org.gcube.informationsystem.model.reference.relations.ConsistsOf; +import org.gcube.informationsystem.resourceregistry.api.contexts.ContextCache; +import org.gcube.informationsystem.resourceregistry.api.exceptions.AvailableInAnotherContextException; +import org.gcube.informationsystem.resourceregistry.api.exceptions.NotFoundException; +import org.gcube.informationsystem.resourceregistry.api.exceptions.ResourceRegistryException; +import org.gcube.informationsystem.resourceregistry.api.exceptions.contexts.ContextNotFoundException; +import org.gcube.informationsystem.resourceregistry.api.exceptions.entities.resource.ResourceNotFoundException; +import org.gcube.informationsystem.resourceregistry.client.ResourceRegistryClient; +import org.gcube.informationsystem.resourceregistry.client.ResourceRegistryClientFactory; +import org.gcube.informationsystem.resourceregistry.publisher.ResourceRegistryPublisher; +import org.gcube.informationsystem.resourceregistry.publisher.ResourceRegistryPublisherFactory; +import org.gcube.informationsystem.utils.ElementMapper; +import org.gcube.resourcemanagement.model.impl.entities.facets.AccessPointFacetImpl; +import org.gcube.resourcemanagement.model.impl.entities.facets.EventFacetImpl; +import org.gcube.resourcemanagement.model.impl.entities.facets.SoftwareFacetImpl; +import org.gcube.resourcemanagement.model.impl.entities.facets.StateFacetImpl; +import org.gcube.resourcemanagement.model.impl.entities.resources.EServiceImpl; +import org.gcube.resourcemanagement.model.impl.properties.ValueSchemaImpl; +import org.gcube.resourcemanagement.model.impl.relations.consistsof.IsIdentifiedByImpl; +import org.gcube.resourcemanagement.model.impl.relations.isrelatedto.ActivatesImpl; +import org.gcube.resourcemanagement.model.reference.entities.facets.AccessPointFacet; +import org.gcube.resourcemanagement.model.reference.entities.facets.EventFacet; +import org.gcube.resourcemanagement.model.reference.entities.facets.MemoryFacet; +import org.gcube.resourcemanagement.model.reference.entities.facets.SoftwareFacet; +import org.gcube.resourcemanagement.model.reference.entities.facets.StateFacet; +import org.gcube.resourcemanagement.model.reference.entities.resources.EService; +import org.gcube.resourcemanagement.model.reference.entities.resources.HostingNode; +import org.gcube.resourcemanagement.model.reference.properties.ValueSchema; +import org.gcube.resourcemanagement.model.reference.relations.consistsof.HasPersistentMemory; +import org.gcube.resourcemanagement.model.reference.relations.consistsof.HasVolatileMemory; +import org.gcube.resourcemanagement.model.reference.relations.consistsof.IsIdentifiedBy; +import org.gcube.resourcemanagement.model.reference.relations.isrelatedto.Activates; +import org.gcube.smartgears.configuration.application.ApplicationConfiguration; +import org.gcube.smartgears.configuration.container.ContainerConfiguration; +import org.gcube.smartgears.context.application.ApplicationContext; +import org.gcube.smartgears.handler.resourceregistry.Constants; +import org.gcube.smartgears.handler.resourceregistry.ContextUtility; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Luca Frosini (ISTI-CNR) + */ +public class EServiceManager { + + private static Logger logger = LoggerFactory.getLogger(HostingNodeManager.class); + + private static List servletExcludes = Arrays.asList("default", "jsp"); + + private ResourceRegistryPublisher resourceRegistryPublisher; + + private EService eService; + + private ApplicationContext applicationContext; + + public EServiceManager(ApplicationContext applicationContext) { + this.applicationContext = applicationContext; + this.resourceRegistryPublisher = ResourceRegistryPublisherFactory.create(); + } + + public void addEServiceToCurrentContext() throws ResourceNotFoundException, ContextNotFoundException, ResourceRegistryException { + String currentToken = SecurityTokenProvider.instance.get(); + UUID contextUUID = ContextUtility.getContextUUID(currentToken); + + /* Trying to get a context which is not the current to properly invoke the addToContext without forcing the operation using addResourceToCurrentContext */ + boolean anotherContextSet = false; + Set startTokens = applicationContext.configuration().startTokens(); + for (String token : startTokens) { + UUID anotherContextUUID = ContextUtility.getContextUUID(token); + if(anotherContextUUID.compareTo(contextUUID)!=0) { + ContextUtility.setContextFromToken(token); + anotherContextSet = true; + break; + } + } + + UUID uuid = UUID.fromString(applicationContext.id()); + try { + if(anotherContextSet) { + resourceRegistryPublisher.addResourceToContext(EService.NAME, uuid, contextUUID, false); + }else { + resourceRegistryPublisher.addResourceToCurrentContext(HostingNode.NAME, uuid, false); + } + logger.info("{} with UUID {} successfully added to context ({})", EService.NAME, uuid, ContextUtility.getCurrentContextName()); + } catch (Exception e) { + logger.error("Unable to add {} with UUID {} to context ({})", EService.NAME, uuid, ContextUtility.getCurrentContextName(), e); + }finally { + ContextUtility.setContextFromToken(currentToken); + } + } + + public void addHostingNodeToCurrentContext() throws ResourceNotFoundException, ContextNotFoundException, ResourceRegistryException { + String currentToken = SecurityTokenProvider.instance.get(); + UUID contextUUID = ContextUtility.getContextUUID(currentToken); + + /* Trying to get a context which is not the current to properly invoke the addToContext without forcing the operation using addResourceToCurrentContext */ + boolean anotherContextSet = false; + Set startTokens = applicationContext.configuration().startTokens(); + for (String token : startTokens) { + UUID anotherContextUUID = ContextUtility.getContextUUID(token); + if(anotherContextUUID.compareTo(contextUUID)!=0) { + ContextUtility.setContextFromToken(token); + anotherContextSet = true; + break; + } + } + + UUID uuid = UUID.fromString(applicationContext.container().id()); + try { + if(anotherContextSet) { + resourceRegistryPublisher.addResourceToContext(HostingNode.NAME, uuid, contextUUID, false); + }else { + resourceRegistryPublisher.addResourceToCurrentContext(HostingNode.NAME, uuid, false); + } + logger.info("{} with UUID {} successfully added to context ({})", HostingNode.NAME, uuid, ContextUtility.getCurrentContextName()); + } catch (Exception e) { + logger.error("Unable to add {} with UUID {} to context ({})", HostingNode.NAME, uuid, ContextUtility.getCurrentContextName(), e); + }finally { + ContextUtility.setContextFromToken(currentToken); + } + } + + public void addToContext(UUID contextUUID) throws ResourceNotFoundException, ContextNotFoundException, ResourceRegistryException { + UUID uuid = UUID.fromString(applicationContext.container().id()); + try { + resourceRegistryPublisher.addToContext(HostingNode.NAME, uuid, contextUUID, false); + logger.info("{} with UUID {} successfully added to context ({})", HostingNode.NAME, uuid, ContextUtility.getCurrentContextName()); + } catch (Exception e) { + logger.error("Unable to add {} with UUID {} to context ({})", HostingNode.NAME, uuid, ContextUtility.getCurrentContextName(), e); + } + } + + public void removeFromCurrentContext() + throws ResourceNotFoundException, ContextNotFoundException, ResourceRegistryException { + UUID uuid = UUID.fromString(applicationContext.container().id()); + try { + resourceRegistryPublisher.removeResourceFromCurrentContext(HostingNode.NAME, uuid, false); + logger.info("{} with UUID {} successfully removed from context ({})", HostingNode.NAME, uuid, + ContextUtility.getCurrentContextName()); + } catch (Exception e) { + logger.error("Unable to remove {} with UUID {} from context ({})", HostingNode.NAME, uuid, ContextUtility.getCurrentContextName(), e); + } + } + + public void removeFromContext(UUID contextUUID) + throws ResourceNotFoundException, ContextNotFoundException, ResourceRegistryException { + String contextFullName = ContextCache.getInstance().getContextFullNameByUUID(contextUUID); + UUID uuid = UUID.fromString(applicationContext.container().id()); + try { + resourceRegistryPublisher.removeResourceFromContext(HostingNode.NAME, uuid, contextUUID, false); + logger.info("{} with UUID {} successfully removed from context ({})", HostingNode.NAME, uuid, contextFullName); + } catch (Exception e) { + logger.error("Unable to remove {} from context ({})", HostingNode.NAME, uuid, contextFullName, e); + } + } + + private String getBaseAddress() { + ApplicationConfiguration configuration = applicationContext.configuration(); + ContainerConfiguration container = applicationContext.container().configuration(); + String baseAddress; + if (configuration.proxied()) { + String protocol = configuration.proxyAddress().protocol(); + String port = configuration.proxyAddress().port() != null ? ":" + configuration.proxyAddress().port() : ""; + + baseAddress = String.format("%s://%s%s%s", protocol, configuration.proxyAddress().hostname(), port, + applicationContext.application().getContextPath()); + } else { + String protocol = container.protocol(); + int port = container.port(); + + baseAddress = String.format("%s://%s:%d%s", protocol, container.hostname(), port, + applicationContext.application().getContextPath()); + } + return baseAddress; + } + + public String getState() { + return applicationContext.lifecycle().state().remoteForm().toLowerCase(); + } + + private StateFacet getStateFacet(StateFacet stateFacet, Date date) { + if (stateFacet == null) { + stateFacet = new StateFacetImpl(); + } + String state = getState(); + stateFacet.setValue(state); + stateFacet.setAdditionalProperty("date", date); + return stateFacet; + } + + private EventFacet getEventFacet(Date date) { + EventFacet eventFacet = new EventFacetImpl(); + eventFacet.setDate(date); + String state = getState(); + eventFacet.setEvent(state); + return eventFacet; + } + + private EService instantiateEService() { + logger.info("Creating {} for {}", EService.NAME, applicationContext.name()); + + ApplicationConfiguration applicationConfiguration = applicationContext.configuration(); + String id = applicationContext.id(); + + UUID uuid = UUID.fromString(id); + EService eService = new EServiceImpl(); + Header header = new HeaderImpl(uuid); + eService.setHeader(header); + + SoftwareFacet softwareFacet = new SoftwareFacetImpl(); + softwareFacet.setDescription(applicationConfiguration.description()); + softwareFacet.setGroup(applicationConfiguration.serviceClass()); + softwareFacet.setName(applicationConfiguration.name()); + softwareFacet.setVersion(applicationConfiguration.version()); + + IsIdentifiedBy isIdentifiedBy = new IsIdentifiedByImpl( + eService, softwareFacet); + eService.addFacet(isIdentifiedBy); + + String baseAddress = getBaseAddress(); + for (ServletRegistration servlet : applicationContext.application().getServletRegistrations().values()) { + if (!servletExcludes.contains(servlet.getName())) { + for (String mapping : servlet.getMappings()) { + + String address = baseAddress + + (mapping.endsWith("*") ? mapping.substring(0, mapping.length() - 2) : mapping); + + AccessPointFacet accessPointFacet = new AccessPointFacetImpl(); + accessPointFacet.setEntryName(servlet.getName()); + accessPointFacet.setEndpoint(URI.create(address)); + ValueSchema valueSchema = new ValueSchemaImpl(); + valueSchema.setValue("gcube-token"); + + accessPointFacet.setAuthorization(valueSchema); + + eService.addFacet(accessPointFacet); + } + } + } + + Date date = Calendar.getInstance().getTime(); + + StateFacet stateFacet = getStateFacet(null, date); + eService.addFacet(stateFacet); + + EventFacet eventFacet = getEventFacet(date);; + eService.addFacet(eventFacet); + + return eService; + } + + public EService createEService() throws ResourceRegistryException { + ResourceRegistryClient resourceRegistryClient = ResourceRegistryClientFactory.create(); + UUID eServiceUUID = UUID.fromString(applicationContext.id()); + try { + ResourceRegistryClientFactory.includeContextsInInstanceHeader(true); + eService = resourceRegistryClient.getInstance(EService.class, eServiceUUID); + updateFacets(); + } catch (NotFoundException e) { + eService = instantiateEService(); + eService = createActivatesRelation(eService).getTarget(); + } catch (AvailableInAnotherContextException e) { + addHostingNodeToCurrentContext(); + try { + eService = resourceRegistryClient.getInstance(EService.class, eServiceUUID); + } catch (AvailableInAnotherContextException ex) { + addEServiceToCurrentContext(); + eService = resourceRegistryClient.getInstance(EService.class, eServiceUUID); + // addToContext() is executed on HostingNode. + // If the EService is still not available we need to create activates + // relation because does not exists otherwise the EService should + // already be in the context due to propagation constraint. + eService = createActivatesRelation(eService).getTarget(); + } + updateFacets(); + } catch (ResourceRegistryException e) { + throw e; + } + return eService; + } + + public EService updateFacets() throws ResourceRegistryException { + logger.debug("Updating {} for {}", EService.NAME, applicationContext.configuration().name()); + + StateFacet stateFacet = null; + EventFacet eventFacet = null; + + Date date = Calendar.getInstance().getTime(); + + List> consistsOfToRemove = new ArrayList<>(); + + List> consistsOfList = eService.getConsistsOf(); + for (ConsistsOf c : consistsOfList) { + if (c.getTarget() instanceof StateFacet) { + stateFacet = (StateFacet) c.getTarget(); + stateFacet = getStateFacet(stateFacet, date); + continue; + } + + if(c.getTarget() instanceof EventFacet) { + eventFacet = (EventFacet) c.getTarget(); + String value = eventFacet.getEvent(); + if(value.compareTo(getState())==0) { + // This facet must be updated (the date must be updated) so it must not be removed from udpate + eventFacet.setDate(date); + continue; + }else { + // This is not the event facet to be updated + // Setting the variable to null so it will be created if the event does not already exists. + eventFacet = null; + } + } + + consistsOfToRemove.add(c); + + } + + // Resource Update has effect only on specified facets. + // Removing the ones that have not to be changed. + consistsOfList.removeAll(consistsOfToRemove); + + + if(eventFacet == null) { + eventFacet = getEventFacet(date); + eService.addFacet(eventFacet); + } + + try { + logger.trace("Updating {} for {} : {}", EService.NAME, applicationContext.configuration().name(), ElementMapper.marshal(eService)); + }catch (Exception e) { + + } + + try { + eService = resourceRegistryPublisher.updateResource(eService); + }catch (ResourceRegistryException e) { + logger.error("Error trying to publish hosting node", e); + } + + return eService; + } + + private Activates createActivatesRelation(EService eService) + throws ResourceRegistryException { + + HostingNode hostingNode = ((HostingNodeManager) applicationContext.container().properties().lookup(Constants.HOSTING_NODE_MANAGER_PROPERTY).value()).getHostingNode(); + + PropagationConstraint propagationConstraint = new PropagationConstraintImpl(); + propagationConstraint.setRemoveConstraint(RemoveConstraint.cascade); + propagationConstraint.setAddConstraint(AddConstraint.propagate); + Activates activates = new ActivatesImpl<>(hostingNode, eService, propagationConstraint); + + try { + logger.trace("Going to create {} and {} for application {} : {}", Activates.NAME, EService.NAME, applicationContext.configuration().name(), ElementMapper.marshal(activates)); + }catch (Exception e) { + + } + + try { + activates = resourceRegistryPublisher.createIsRelatedTo(activates); + hostingNode.attachResource(activates); + } catch (NotFoundException e) { + logger.error("THIS IS REALLY STRANGE. YOU SHOULD NOT BE HERE. Error while creating {}.", activates, e); + throw e; + } catch (ResourceRegistryException e) { + logger.error("Error while creating {}", activates, e); + throw e; + } + + return activates; + + } + +// public void removeEService() throws ResourceRegistryException { +// try { +// resourceRegistryPublisher.delete(eService); +// } catch (ResourceRegistryException e) { +// logger.error("Unable to delete {}. Going to set the state to {}", applicationContext.name(), getState()); +// updateFacets(); +// } +// } + + public Map getContextsUUID() throws Exception { + return resourceRegistryPublisher.getResourceContexts(eService); + } + +} diff --git a/src/main/java/org/gcube/smartgears/handler/resourceregistry/resourcemanager/HostingNodeManager.java b/src/main/java/org/gcube/smartgears/handler/resourceregistry/resourcemanager/HostingNodeManager.java new file mode 100644 index 0000000..59a60ae --- /dev/null +++ b/src/main/java/org/gcube/smartgears/handler/resourceregistry/resourcemanager/HostingNodeManager.java @@ -0,0 +1,659 @@ +package org.gcube.smartgears.handler.resourceregistry.resourcemanager; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.lang.management.ManagementFactory; +import java.lang.management.OperatingSystemMXBean; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.nio.file.FileStore; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; +import java.util.UUID; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.management.AttributeNotFoundException; +import javax.management.InstanceNotFoundException; +import javax.management.MBeanException; +import javax.management.MBeanServer; +import javax.management.MalformedObjectNameException; +import javax.management.ObjectName; +import javax.management.ReflectionException; + +import org.gcube.com.fasterxml.jackson.databind.JsonNode; +import org.gcube.common.authorization.library.provider.SecurityTokenProvider; +import org.gcube.informationsystem.model.impl.properties.HeaderImpl; +import org.gcube.informationsystem.model.reference.entities.Facet; +import org.gcube.informationsystem.model.reference.entities.Resource; +import org.gcube.informationsystem.model.reference.properties.Header; +import org.gcube.informationsystem.model.reference.relations.ConsistsOf; +import org.gcube.informationsystem.resourceregistry.api.contexts.ContextCache; +import org.gcube.informationsystem.resourceregistry.api.exceptions.AvailableInAnotherContextException; +import org.gcube.informationsystem.resourceregistry.api.exceptions.NotFoundException; +import org.gcube.informationsystem.resourceregistry.api.exceptions.ResourceRegistryException; +import org.gcube.informationsystem.resourceregistry.api.exceptions.contexts.ContextNotFoundException; +import org.gcube.informationsystem.resourceregistry.api.exceptions.entities.resource.ResourceNotFoundException; +import org.gcube.informationsystem.resourceregistry.client.ResourceRegistryClient; +import org.gcube.informationsystem.resourceregistry.client.ResourceRegistryClientFactory; +import org.gcube.informationsystem.resourceregistry.publisher.ResourceRegistryPublisher; +import org.gcube.informationsystem.resourceregistry.publisher.ResourceRegistryPublisherFactory; +import org.gcube.informationsystem.utils.ElementMapper; +import org.gcube.resourcemanagement.model.impl.entities.facets.CPUFacetImpl; +import org.gcube.resourcemanagement.model.impl.entities.facets.EventFacetImpl; +import org.gcube.resourcemanagement.model.impl.entities.facets.LocationFacetImpl; +import org.gcube.resourcemanagement.model.impl.entities.facets.MemoryFacetImpl; +import org.gcube.resourcemanagement.model.impl.entities.facets.NetworkingFacetImpl; +import org.gcube.resourcemanagement.model.impl.entities.facets.SimplePropertyFacetImpl; +import org.gcube.resourcemanagement.model.impl.entities.facets.SoftwareFacetImpl; +import org.gcube.resourcemanagement.model.impl.entities.facets.StateFacetImpl; +import org.gcube.resourcemanagement.model.impl.entities.resources.HostingNodeImpl; +import org.gcube.resourcemanagement.model.impl.relations.consistsof.HasPersistentMemoryImpl; +import org.gcube.resourcemanagement.model.impl.relations.consistsof.HasVolatileMemoryImpl; +import org.gcube.resourcemanagement.model.impl.relations.consistsof.IsIdentifiedByImpl; +import org.gcube.resourcemanagement.model.reference.entities.facets.CPUFacet; +import org.gcube.resourcemanagement.model.reference.entities.facets.EventFacet; +import org.gcube.resourcemanagement.model.reference.entities.facets.LocationFacet; +import org.gcube.resourcemanagement.model.reference.entities.facets.MemoryFacet; +import org.gcube.resourcemanagement.model.reference.entities.facets.MemoryFacet.MemoryUnit; +import org.gcube.resourcemanagement.model.reference.entities.facets.NetworkingFacet; +import org.gcube.resourcemanagement.model.reference.entities.facets.SimplePropertyFacet; +import org.gcube.resourcemanagement.model.reference.entities.facets.SoftwareFacet; +import org.gcube.resourcemanagement.model.reference.entities.facets.StateFacet; +import org.gcube.resourcemanagement.model.reference.entities.resources.HostingNode; +import org.gcube.resourcemanagement.model.reference.relations.consistsof.HasPersistentMemory; +import org.gcube.resourcemanagement.model.reference.relations.consistsof.HasVolatileMemory; +import org.gcube.resourcemanagement.model.reference.relations.consistsof.IsIdentifiedBy; +import org.gcube.smartgears.configuration.container.ContainerConfiguration; +import org.gcube.smartgears.configuration.container.Site; +import org.gcube.smartgears.configuration.library.SmartGearsConfiguration; +import org.gcube.smartgears.context.container.ContainerContext; +import org.gcube.smartgears.handler.resourceregistry.ContextUtility; +import org.gcube.smartgears.lifecycle.container.ContainerState; +import org.gcube.smartgears.provider.ProviderFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Luca Frosini (ISTI-CNR) + */ +public class HostingNodeManager { + + private static Logger logger = LoggerFactory.getLogger(HostingNodeManager.class); + + public static final String MEMORY_TYPE = "memoryType"; + public static final String MEMORY_TYPE_RAM = "RAM"; + public static final String MEMORY_TYPE_JVM = "JVM"; + public static final String JVM_MAX_MEMORY = "jvmMaxMemory"; + + public static final String MESSAGE = "message"; + + private ContainerContext containerContext; + private ResourceRegistryPublisher resourceRegistryPublisher; + private HostingNode hostingNode; + + public HostingNodeManager(ContainerContext containerContext) { + this.containerContext = containerContext; + this.resourceRegistryPublisher = ResourceRegistryPublisherFactory.create(); + } + + public HostingNode getHostingNode() { + return hostingNode; + } + + public void addToCurrentContext() throws ResourceNotFoundException, ContextNotFoundException, ResourceRegistryException { + String currentToken = SecurityTokenProvider.instance.get(); + UUID contextUUID = ContextUtility.getContextUUID(currentToken); + + /* Trying to get a context which is not the current to properly invoke the addToContext without forcing the operation using addResourceToCurrentContext */ + boolean anotherContextSet = false; + List startTokens = containerContext.configuration().startTokens(); + for (String token : startTokens) { + UUID anotherContextUUID = ContextUtility.getContextUUID(token); + if(anotherContextUUID.compareTo(contextUUID)!=0) { + ContextUtility.setContextFromToken(token); + anotherContextSet = true; + break; + } + } + + UUID uuid = UUID.fromString(containerContext.id()); + try { + if(anotherContextSet) { + resourceRegistryPublisher.addToContext(HostingNode.NAME, uuid, contextUUID, false); + }else { + resourceRegistryPublisher.addResourceToCurrentContext(HostingNode.NAME, uuid, false); + } + logger.info("{} with UUID {} successfully added to context ({})", HostingNode.NAME, uuid, ContextUtility.getCurrentContextName()); + } catch (Exception e) { + logger.error("Unable to add {} with UUID {} to context ({})", HostingNode.NAME, uuid, ContextUtility.getCurrentContextName(), e); + } + } + + public void addToContext(UUID contextUUID) throws ResourceNotFoundException, ContextNotFoundException, ResourceRegistryException { + UUID uuid = UUID.fromString(containerContext.id()); + try { + resourceRegistryPublisher.addToContext(HostingNode.NAME, uuid, contextUUID, false); + logger.info("{} with UUID {} successfully added to context ({})", HostingNode.NAME, uuid, ContextUtility.getCurrentContextName()); + } catch (Exception e) { + logger.error("Unable to add {} with UUID {} to context ({})", HostingNode.NAME, uuid, ContextUtility.getCurrentContextName(), e); + } + } + + public void removeFromCurrentContext() + throws ResourceNotFoundException, ContextNotFoundException, ResourceRegistryException { + UUID uuid = UUID.fromString(containerContext.id()); + try { + resourceRegistryPublisher.removeResourceFromCurrentContext(HostingNode.NAME, uuid, false); + logger.info("{} with UUID {} successfully removed from context ({})", HostingNode.NAME, uuid, + ContextUtility.getCurrentContextName()); + } catch (Exception e) { + logger.error("Unable to remove {} with UUID {} from context ({})", HostingNode.NAME, uuid, ContextUtility.getCurrentContextName(), e); + } + } + + public void removeFromContext(UUID contextUUID) + throws ResourceNotFoundException, ContextNotFoundException, ResourceRegistryException { + String contextFullName = ContextCache.getInstance().getContextFullNameByUUID(contextUUID); + UUID uuid = UUID.fromString(containerContext.id()); + try { + resourceRegistryPublisher.removeResourceFromContext(HostingNode.NAME, uuid, contextUUID, false); + logger.info("{} with UUID {} successfully removed from context ({})", HostingNode.NAME, uuid, contextFullName); + } catch (Exception e) { + logger.error("Unable to remove {} from context ({})", HostingNode.NAME, uuid, contextFullName, e); + } + } + + public HostingNode updateFacets() throws ResourceRegistryException { + logger.debug("Updating {}", HostingNode.NAME); + + MemoryFacet ramFacet = null; + MemoryFacet jvmMemoryFacet = null; + MemoryFacet disk = null; + StateFacet stateFacet = null; + EventFacet eventFacet = null; + + Date date = Calendar.getInstance().getTime(); + + List> consistsOfToRemove = new ArrayList<>(); + + List> consistsOfList = hostingNode.getConsistsOf(); + for (ConsistsOf c : consistsOfList) { + if (c.getTarget() instanceof StateFacet) { + stateFacet = (StateFacet) c.getTarget(); + stateFacet = getStateFacet(stateFacet, date); + continue; + } + + if (c instanceof HasVolatileMemory) { + String memoryType = (String) c.getAdditionalProperty(MEMORY_TYPE); + if (memoryType.compareTo(MEMORY_TYPE_RAM) == 0) { + ramFacet = (MemoryFacet) c.getTarget(); + ramFacet = getRamInfo(ramFacet); + continue; + } + + if (memoryType.compareTo(MEMORY_TYPE_JVM) == 0) { + jvmMemoryFacet = (MemoryFacet) c.getTarget(); + jvmMemoryFacet = getJVMMemoryInfo(jvmMemoryFacet); + continue; + } + + } + + if (c instanceof HasPersistentMemory) { + disk = (MemoryFacet) c.getTarget(); + disk = getDiskSpace(disk); + continue; + } + + if(c.getTarget() instanceof EventFacet) { + eventFacet = (EventFacet) c.getTarget(); + String value = eventFacet.getEvent(); + if(value.compareTo(getState())==0) { + // This facet must be updated (the date must be updated) so it must not be removed from udpate + eventFacet.setDate(date); + continue; + }else { + // This is not the event facet to be updated + // Setting the variable to null so it will be created if the event does not already exists. + eventFacet = null; + } + } + + consistsOfToRemove.add(c); + + } + + // Resource Update has effect only on specified facets. + // Removing the ones that have not to be changed. + consistsOfList.removeAll(consistsOfToRemove); + + if(eventFacet == null) { + eventFacet = getEventFacet(date); + hostingNode.addFacet(eventFacet); + } + + try { + hostingNode = resourceRegistryPublisher.updateResource(hostingNode); + } catch (ResourceRegistryException e) { + logger.error("error trying to publish hosting node", e); + } + + return hostingNode; + } + + public static SoftwareFacet getOperativeSystem() { + SoftwareFacet osSoftwareFacet = new SoftwareFacetImpl(); + OperatingSystemMXBean mxbean = ManagementFactory.getOperatingSystemMXBean(); + String osName = mxbean.getName(); + osSoftwareFacet.setGroup(osName); // softwareFacet.setGroup(System.getProperty("os.name")); + osSoftwareFacet.setName(mxbean.getArch()); // softwareFacet.setName(System.getProperty("os.arch")); + osSoftwareFacet.setVersion(mxbean.getVersion()); // softwareFacet.setVersion(System.getProperty("os.version")); + + JsonNode jsonNode = ElementMapper.getObjectMapper().valueToTree(osSoftwareFacet); + Set fieldNames = new TreeSet<>(); + Iterator it = jsonNode.fieldNames(); + while (it.hasNext()) fieldNames.add(it.next()); + + if(osName.compareTo("Linux")==0) { + // Adding Linux Distribution Info + LinuxDistributionInfo linuxDistributionInfo = new LinuxDistributionInfo(); + Map map = linuxDistributionInfo.getInfo(); + Set keys = map.keySet(); + for(String key : keys) { + String k = key; + if(fieldNames.contains(key)) { + k = "linuxDistribution-" + k; + } + osSoftwareFacet.setAdditionalProperty(k, map.get(key)); + } + } + + return osSoftwareFacet; + } + + private HostingNode instantiateHostingNode() { + logger.info("Creating {}", HostingNode.NAME); + + ContainerConfiguration containerConfiguration = containerContext.configuration(); + String id = containerContext.id(); + + UUID uuid = UUID.fromString(id); + HostingNode hostingNode = new HostingNodeImpl(); + Header header = new HeaderImpl(uuid); + hostingNode.setHeader(header); + + NetworkingFacet networkingFacet = new NetworkingFacetImpl(); + try { + networkingFacet.setIPAddress(InetAddress.getLocalHost().getHostAddress()); + } catch (UnknownHostException e) { + logger.warn("unable to detect the IP address of the host"); + } + String hostname = containerConfiguration.hostname(); + networkingFacet.setHostName(hostname); + networkingFacet.setDomainName(getDomain(hostname)); + + networkingFacet.setAdditionalProperty("Port", containerConfiguration.port()); + IsIdentifiedBy isIdentifiedBy = new IsIdentifiedByImpl<>(hostingNode, + networkingFacet, null); + hostingNode.addFacet(isIdentifiedBy); + + List cpuFacets = getCPUFacets(); + for (CPUFacet cpuFacet : cpuFacets) { + hostingNode.addFacet(cpuFacet); + } + + SoftwareFacet osSoftwareFacet = getOperativeSystem(); + hostingNode.addFacet(osSoftwareFacet); + + + SmartGearsConfiguration config = ProviderFactory.provider().smartgearsConfiguration(); + SoftwareFacet smartgearsSoftwareFacet = new SoftwareFacetImpl(); + smartgearsSoftwareFacet.setGroup("gCube"); + smartgearsSoftwareFacet.setName("SmartGears"); + smartgearsSoftwareFacet.setVersion(config.version()); + hostingNode.addFacet(smartgearsSoftwareFacet); + + SoftwareFacet smartgearsDistributionSoftwareFacet = new SoftwareFacetImpl(); + smartgearsDistributionSoftwareFacet.setGroup("gCube"); + smartgearsDistributionSoftwareFacet.setName("SmartGearsDistribution"); + String smartgearsDistributionVersion = containerConfiguration.properties().get("SmartGearsDistribution"); + smartgearsDistributionSoftwareFacet.setVersion(smartgearsDistributionVersion); + smartgearsDistributionSoftwareFacet.setAdditionalProperty("publication-frequency", + String.valueOf(containerConfiguration.publicationFrequency())); + hostingNode.addFacet(smartgearsDistributionSoftwareFacet); + + + SoftwareFacet javaSoftwareFacet = new SoftwareFacetImpl(); + javaSoftwareFacet.setGroup(System.getProperty("java.vendor")); + javaSoftwareFacet.setName("Java"); + javaSoftwareFacet.setVersion(System.getProperty("java.version")); + javaSoftwareFacet.setAdditionalProperty("java.vendor.url", System.getProperty("java.vendor.url")); + javaSoftwareFacet.setAdditionalProperty("java.specification.version", System.getProperty("java.specification.version")); + hostingNode.addFacet(javaSoftwareFacet); + + SimplePropertyFacet simplePropertyFacet = addEnvironmentVariables(containerConfiguration); + hostingNode.addFacet(simplePropertyFacet); + + Date date = Calendar.getInstance().getTime(); + + StateFacet stateFacet = getStateFacet(null, date); + hostingNode.addFacet(stateFacet); + + EventFacet eventFacet = getEventFacet(date); + hostingNode.addFacet(eventFacet); + + MemoryFacet ramFacet = getRamInfo(null); + HasVolatileMemory hasVolatileRAMMemory = new HasVolatileMemoryImpl( + hostingNode, ramFacet, null); + hasVolatileRAMMemory.setAdditionalProperty(MEMORY_TYPE, MEMORY_TYPE_RAM); + hostingNode.addFacet(hasVolatileRAMMemory); + + MemoryFacet jvmMemoryFacet = getJVMMemoryInfo(null); + HasVolatileMemory hasVolatileJVMMemory = new HasVolatileMemoryImpl( + hostingNode, jvmMemoryFacet, null); + hasVolatileJVMMemory.setAdditionalProperty(MEMORY_TYPE, MEMORY_TYPE_JVM); + hostingNode.addFacet(hasVolatileJVMMemory); + + MemoryFacet diskFacet = getDiskSpace(null); + HasPersistentMemory hasPersistentMemory = new HasPersistentMemoryImpl( + hostingNode, diskFacet, null); + hostingNode.addFacet(hasPersistentMemory); + + LocationFacet locationFacet = new LocationFacetImpl(); + Site site = containerConfiguration.site(); + locationFacet.setCountry(site.country()); + locationFacet.setLocation(site.location()); + locationFacet.setLatitude(site.latitude()); + locationFacet.setLongitude(site.longitude()); + hostingNode.addFacet(locationFacet); + + logger.info("{} with UUID {} instantiated", HostingNode.NAME, uuid); + return hostingNode; + } + + public HostingNode createHostingNode() throws ResourceRegistryException { + ResourceRegistryClient resourceRegistryClient = ResourceRegistryClientFactory.create(); + UUID uuid = UUID.fromString(containerContext.id()); + try { + hostingNode = resourceRegistryClient.getInstance(HostingNode.class, uuid); + hostingNode = updateFacets(); + } catch (NotFoundException e) { + hostingNode = instantiateHostingNode(); + hostingNode = resourceRegistryPublisher.createResource(hostingNode); + } catch (AvailableInAnotherContextException e) { + addToCurrentContext(); + hostingNode = resourceRegistryClient.getInstance(HostingNode.class, uuid); + } catch (ResourceRegistryException e) { + logger.error("", e); + } + return hostingNode; + } + + public String getState() { + return containerContext.lifecycle().state().remoteForm().toLowerCase(); + } + + + private StateFacet getStateFacet(StateFacet stateFacet, Date date) { + if (stateFacet == null) { + stateFacet = new StateFacetImpl(); + } + String state = getState(); + stateFacet.setValue(state); + stateFacet.setAdditionalProperty("date", date); + return stateFacet; + } + + private EventFacet getEventFacet(Date date) { + EventFacet eventFacet = new EventFacetImpl(); + eventFacet.setDate(date); + String state = getState(); + eventFacet.setEvent(state); + return eventFacet; + } + + private MemoryFacet getDiskSpace(MemoryFacet memoryFacet) { + if (memoryFacet == null) { + memoryFacet = new MemoryFacetImpl(); + } + + long free = 0; + long total = 0; + try { + FileStore fileStore = Files + .getFileStore(Paths.get(containerContext.configuration().persistence().location())); + free = fileStore.getUsableSpace() / 1048576; // 1048576 = 1024*1024 + // user to convert + // bytes in MByte + total = fileStore.getTotalSpace() / 1048576; // 1048576 = 1024*1024 + // user to convert + // bytes in MByte + } catch (IOException ioe) { + logger.warn("Unable to detect disk space information", ioe); + memoryFacet.setAdditionalProperty(MESSAGE, "Unable to detect disk space information."); + } + + memoryFacet.setUnit(MemoryUnit.MB); + memoryFacet.setSize(total); + memoryFacet.setUsed(total - free); + + return memoryFacet; + } + + private static final long BYTE_TO_MB = 1024 * 1024; + + private MemoryFacet getRamInfo(MemoryFacet memoryFacet) { + if (memoryFacet == null) { + memoryFacet = new MemoryFacetImpl(); + } + + /* + * OperatingSystemMXBean mxbean = ManagementFactory.getOperatingSystemMXBean(); + * com.sun.management.OperatingSystemMXBean sunmxbean = + * (com.sun.management.OperatingSystemMXBean) mxbean; long freeMemory = + * sunmxbean.getFreePhysicalMemorySize() / 1048576; // in MB long totalMemory = + * sunmxbean.getTotalPhysicalMemorySize() / 1048576; // in MB + */ + + MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer(); + long freeMemory; + try { + freeMemory = Long.parseLong(mBeanServer + .getAttribute(new ObjectName("java.lang", "type", "OperatingSystem"), "FreePhysicalMemorySize") + .toString()) / BYTE_TO_MB; + } catch (NumberFormatException | InstanceNotFoundException | AttributeNotFoundException + | MalformedObjectNameException | ReflectionException | MBeanException e) { + logger.warn( + "Unable to get free memory from Operating System. Going to get JVM Memory. Better than nothing"); + long allocatedMemory = (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()); + freeMemory = Runtime.getRuntime().maxMemory() - allocatedMemory; + } + long totalMemory; + try { + totalMemory = Long.parseLong(mBeanServer + .getAttribute(new ObjectName("java.lang", "type", "OperatingSystem"), "TotalPhysicalMemorySize") + .toString()) / BYTE_TO_MB; + } catch (NumberFormatException | InstanceNotFoundException | AttributeNotFoundException + | MalformedObjectNameException | ReflectionException | MBeanException e) { + logger.warn("Unable to total memory from Operating System. Going to get JVM Memory. Better than nothing"); + totalMemory = Runtime.getRuntime().maxMemory(); + } + + memoryFacet.setUnit(MemoryUnit.MB); + memoryFacet.setSize(totalMemory); + memoryFacet.setUsed(totalMemory - freeMemory); + + return memoryFacet; + } + + private MemoryFacet getJVMMemoryInfo(MemoryFacet memoryFacet) { + if (memoryFacet == null) { + memoryFacet = new MemoryFacetImpl(); + } + + /* 1048576 = 1024*1024 used to convert bytes in MByte */ + long jvmFreeMemory = Runtime.getRuntime().freeMemory() / 1048576; + long jvmTotalMemory = Runtime.getRuntime().totalMemory() / 1048576; + long jvmMaxMemory = Runtime.getRuntime().maxMemory() / 1048576; + + memoryFacet.setUnit(MemoryUnit.MB); + memoryFacet.setSize(jvmTotalMemory); + memoryFacet.setUsed(jvmTotalMemory - jvmFreeMemory); + memoryFacet.setAdditionalProperty(JVM_MAX_MEMORY, jvmMaxMemory); + + return memoryFacet; + } + + private static String sanitizeKey(String key) { + return key.trim().replace(" ", "_"); + } + + private SimplePropertyFacet addEnvironmentVariables(ContainerConfiguration containerConfiguration) { + + Map map = new HashMap(); + map.putAll(containerConfiguration.properties()); + map.putAll(System.getenv()); + + SimplePropertyFacet simplePropertyFacet = new SimplePropertyFacetImpl(); + simplePropertyFacet.setName("ENVIRONMENT_VARIABLES"); + simplePropertyFacet.setValue(""); + + for (Map.Entry entry : map.entrySet()) { + String varname = entry.getKey(); + if ((varname.compareToIgnoreCase("CLASSPATH") == 0) || (varname.compareToIgnoreCase("PATH") == 0) + || (varname.contains("SSH")) || (varname.contains("MAIL")) + || (varname.compareToIgnoreCase("LS_COLORS") == 0)) { + continue; + } + simplePropertyFacet.setAdditionalProperty(sanitizeKey(entry.getKey()), entry.getValue()); + } + + return simplePropertyFacet; + } + + private static String getDomain(String hostname) { + try { + Pattern pattern = Pattern.compile("([0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3})"); + Matcher regexMatcher = pattern.matcher(hostname); + if (regexMatcher.matches()) { // it's an IP address, nothing to trim + return hostname; + } + return hostname.substring(hostname.indexOf(".") + 1); + } catch (Exception e) { + logger.warn("Error while getting domain from hostname"); + return hostname; + } + } + + public static final String CPU_PROCESSOR = "processor"; + public static final String CPU_VENDOR_ID = "vendor_id"; + public static final String CPU_MODEL_NAME = "model name"; + public static final String CPU_CPU_MHZ = "cpu MHz"; + public static final String CPU_MODEL_T = "model\t"; + public static final String CPU_MODEL_B = "model\b"; + public static final String CPU_MODEL_NUMBER = "modelNumber"; + + public static List getCPUFacets() { + + List cpuFacets = new ArrayList<>(); + + File file = new File("/proc/cpuinfo"); + + if (!file.exists()) { + logger.warn("cannot acquire CPU info (no /proc/cpuinfo)"); + return cpuFacets; + } + + BufferedReader input = null; + + try { + input = new BufferedReader(new FileReader(file)); + + String line = null; + + CPUFacet cpuFacet = null; + + while ((line = input.readLine()) != null) { + + if ((line.startsWith(CPU_PROCESSOR))) { // add the current + // processor to the map + cpuFacet = new CPUFacetImpl(); + cpuFacets.add(cpuFacet); + } + + try { + if (line.contains(CPU_VENDOR_ID)) { + cpuFacet.setVendor(line.split(":")[1].trim()); + continue; + } + } catch (Exception e) { + continue; + } + + try { + if (line.contains(CPU_MODEL_NAME)) { + cpuFacet.setModel(line.split(":")[1].trim()); + continue; + } + } catch (Exception e) { + continue; + } + + try { + if (line.contains(CPU_CPU_MHZ)) { + cpuFacet.setClockSpeed(line.split(":")[1].trim()); + continue; + } + } catch (Exception e) { + continue; + } + + try { + if ((line.contains(CPU_MODEL_T)) || (line.contains(CPU_MODEL_B))) { + cpuFacet.setAdditionalProperty(CPU_MODEL_NUMBER, line.split(":")[1].trim()); + continue; + } + } catch (Exception e) { + continue; + } + + try { + String[] nameValue = line.split(":"); + cpuFacet.setAdditionalProperty(sanitizeKey(nameValue[0]), line.split(":")[1].trim()); + } catch (Exception e) { + + } + + } + } catch (Exception e) { + logger.warn("unable to acquire CPU info", e); + } finally { + if (input != null) { + try { + input.close(); + } catch (IOException e) { + logger.warn("unable to close stream", e); + } + } + } + return cpuFacets; + } + + public Map getContextsUUID() throws Exception { + return resourceRegistryPublisher.getResourceContexts(hostingNode); + } + +} diff --git a/src/main/java/org/gcube/smartgears/handler/resourceregistry/resourcemanager/LinuxDistributionInfo.java b/src/main/java/org/gcube/smartgears/handler/resourceregistry/resourcemanager/LinuxDistributionInfo.java new file mode 100644 index 0000000..db149ca --- /dev/null +++ b/src/main/java/org/gcube/smartgears/handler/resourceregistry/resourcemanager/LinuxDistributionInfo.java @@ -0,0 +1,93 @@ +package org.gcube.smartgears.handler.resourceregistry.resourcemanager; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.HashMap; +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Luca Frosini (ISTI-CNR) + */ +public class LinuxDistributionInfo { + + private static final Logger logger = LoggerFactory.getLogger(LinuxDistributionInfo.class); + + public static final String LSB_RELEASE_COMMAND = "lsb_release -a"; + public static final String OS_RELEASE_FILE_PATH = "/etc/os-release"; + + protected Map info; + + protected Map getInfoViaLsbReleaseCommand() throws IOException { + logger.trace("Going to exec {}", LSB_RELEASE_COMMAND); + Process process = Runtime.getRuntime().exec(LSB_RELEASE_COMMAND); + BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream())); + Map map = parseBufferedReader(bufferedReader); + bufferedReader.close(); + return map; + } + + private Map parseBufferedReader(BufferedReader bufferedReader) throws IOException { + Map map = new HashMap<>(); + String line = ""; + while ((line = bufferedReader.readLine()) != null) { + String[] nameValue = parseLine(line); + map.put(nameValue[0], nameValue[1]); + } + return map; + } + + private String[] parseLine(String line) { + String[] splitted = line.split("="); + if (splitted.length < 2) { + splitted = line.split(":"); + } + String[] ret = new String[2]; + ret[0] = splitted[0].trim(); + ret[1] = splitted[1].trim().replace("\"", ""); + return ret; + } + + private Map getInfoViaFile(File file) throws IOException { + logger.trace("Going to read file {}", file.getAbsolutePath()); + BufferedReader bufferedReader = new BufferedReader(new FileReader(file)); + Map map = parseBufferedReader(bufferedReader); + bufferedReader.close(); + return map; + + } + + protected Map getInfoViaOsReleaseFile() throws IOException { + File osReleaseFile = new File(OS_RELEASE_FILE_PATH); + return getInfoViaFile(osReleaseFile); + } + + private Map retriveInfo() { + try { + return getInfoViaLsbReleaseCommand(); + } catch (IOException e) { + + } + + try { + return getInfoViaOsReleaseFile(); + }catch (IOException e) { + + } + + return null; + } + + public Map getInfo() { + if (info == null) { + info = retriveInfo(); + } + return info; + } + +}