package org.gcube.smartgears.provider; import static org.gcube.smartgears.Constants.configuration_file_path; import static org.gcube.smartgears.Constants.container_configuraton_file_path; import static org.gcube.smartgears.Constants.container_profile_file_path; import static org.gcube.smartgears.Constants.default_extensions_file_path; import static org.gcube.smartgears.Constants.extensions_file_path; import static org.gcube.smartgears.Constants.ghn_home_env; import static org.gcube.smartgears.Constants.ghn_home_property; import static org.gcube.smartgears.Constants.library_configuration_file_path; import static org.gcube.smartgears.Constants.profile_file_path; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.io.ObjectInputStream; import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.UUID; import java.util.stream.Collectors; import javax.servlet.ServletContext; import org.gcube.common.events.Hub; import org.gcube.common.events.impl.DefaultHub; import org.gcube.common.security.credentials.Credentials; import org.gcube.smartgears.configuration.application.ApplicationConfiguration; import org.gcube.smartgears.configuration.application.ApplicationConfigurationBinder; import org.gcube.smartgears.configuration.application.ApplicationExtensions; import org.gcube.smartgears.configuration.application.ApplicationHandlers; import org.gcube.smartgears.configuration.application.BridgedApplicationConfiguration; import org.gcube.smartgears.configuration.container.ContainerConfiguration; import org.gcube.smartgears.configuration.container.ContainerConfigurationBinder; import org.gcube.smartgears.configuration.library.SmartGearsConfiguration; import org.gcube.smartgears.configuration.library.SmartGearsConfigurationBinder; import org.gcube.smartgears.context.Properties; import org.gcube.smartgears.context.application.ApplicationContext; import org.gcube.smartgears.context.application.DefaultApplicationContext; import org.gcube.smartgears.context.container.ContainerContext; import org.gcube.smartgears.context.container.DefaultContainerContext; import org.gcube.smartgears.handlers.container.ContainerHandler; import org.gcube.smartgears.lifecycle.application.ApplicationLifecycle; import org.gcube.smartgears.lifecycle.container.ContainerLifecycle; import org.gcube.smartgears.publishing.Publisher; import org.gcube.smartgears.publishing.SmartgearsProfilePublisher; import org.gcube.smartgears.security.AuthorizationProvider; import org.gcube.smartgears.security.AuthorizationProviderFactory; import org.gcube.smartgears.utils.Utils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import io.github.classgraph.ClassGraph; import io.github.classgraph.ClassInfo; import io.github.classgraph.ClassInfoList; import io.github.classgraph.ScanResult; /** * Default implementation of the {@link Provider} interface. * * @author Fabio Simeoni * */ public class DefaultProvider implements Provider { private static Logger log = LoggerFactory.getLogger(Provider.class); private ContainerContext containerContext; // TODO: do the same with applicationContext (with a map) private File configFile = null; protected DefaultProvider(File configFile) { this.configFile = configFile; } List publishers; protected DefaultProvider() { }; @Override public ContainerContext containerContext() { if (containerContext == null) { ContainerConfiguration configuration = containerConfiguration(); Hub hub = new DefaultHub(); ContainerLifecycle lifecycle = new ContainerLifecycle(hub); File file = configuration.persistence().file(container_profile_file_path); String id = null; if (file.exists()) { log.info("loading persisted state for container"); try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file))) { id = (String) ois.readObject(); } catch (Exception e) { log.error("error loading persisted state, creating new uuid", e); } } if (id == null) { id = UUID.randomUUID().toString(); log.info("container id created is {}", id); } AuthorizationProviderFactory authfactory = configuration.getauthorizationConfiguration() .getAuthProviderFactory(); Credentials credentials = configuration.getauthorizationConfiguration().getCredentials(); AuthorizationProvider authProvider = authfactory.connect(credentials); containerContext = new DefaultContainerContext(id, configuration, hub, lifecycle, authProvider, new Properties()); } return containerContext; } @Override public List containerHandlers() { try { ContainerConfigurationBinder binder = new ContainerConfigurationBinder(); ClassLoader currentClassLoader = Thread.currentThread().getContextClassLoader(); if (currentClassLoader.getParent() != null && !currentClassLoader.getParent().equals(ClassLoader.getSystemClassLoader())) { log.trace("probably i'm in a webapp classloader"); currentClassLoader = currentClassLoader.getParent(); } List defaultHandlers = binder.bindHandlers(currentClassLoader); return defaultHandlers; } catch (RuntimeException e) { throw new RuntimeException("cannot install container handlers (see cause) ", e); } } @Override public ApplicationContext contextFor(ContainerContext context, ServletContext application) { ApplicationConfiguration configuration = null; ApplicationConfiguration embedded = configurationFor(application); ApplicationConfiguration external = context.configuration().app(application.getContextPath()); // shouldn't happen: management shouldn't have started at all if (embedded == null && external == null) throw new AssertionError("application @ " + application.getContextPath() + " is not distributed with " + configuration_file_path + " and there is no external configuration for it in " + container_configuraton_file_path); // no embedded configuration if (embedded == null) { configuration = external; log.info("loaded configuration for application " + configuration.name() + " from " + container_configuraton_file_path); } else { configuration = embedded; if (external == null) log.info("loaded configuration for application " + configuration.name() + " from " + configuration_file_path); else { configuration.merge(external); log.info("loaded configuration for application " + configuration.name() + " from " + configuration_file_path + " and " + container_configuraton_file_path); } } ApplicationConfiguration bridgedConfiguration = new BridgedApplicationConfiguration(context.configuration(), configuration); Hub hub = new DefaultHub(); ApplicationLifecycle lifecycle = new ApplicationLifecycle(hub, configuration.name()); File file = bridgedConfiguration.persistence().file(profile_file_path); String id = null; if (file.exists()) { log.info("loading persisted state for application {}", application.getContextPath()); try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file))) { id = (String) ois.readObject(); } catch (Exception e) { log.error("error loading persisted state, creating new uuid", e); } } if (id == null) id = UUID.randomUUID().toString(); return new DefaultApplicationContext(id, context, application, bridgedConfiguration, hub, lifecycle, new Properties()); } @Override public ApplicationHandlers handlersFor(ApplicationContext context) { try { ApplicationConfigurationBinder binder = new ApplicationConfigurationBinder(); // searching for smartegars related application handlers in the common // classloader ClassLoader currentClassLoader = Thread.currentThread().getContextClassLoader(); if (currentClassLoader.getParent() != null && !currentClassLoader.getParent().equals(ClassLoader.getSystemClassLoader())) { log.trace("probably i'm in a webapp classloader"); currentClassLoader = currentClassLoader.getParent(); } ApplicationHandlers defaultHandlers = binder.bindHandlers(currentClassLoader); return defaultHandlers; } catch (RuntimeException e) { throw new RuntimeException("cannot install handlers for application @ " + context.name() + " (see cause) ", e); } } @Override public ApplicationExtensions extensionsFor(ApplicationContext context) { try { InputStream config = context.application().getResourceAsStream(extensions_file_path); if (config == null) { log.trace("{} uses default extensions as it does not include {}", context.name(), extensions_file_path); // it's in a library, using config = getClass().getResourceAsStream(default_extensions_file_path); if (config == null) throw new IllegalStateException( "invalid distribution: cannot find " + default_extensions_file_path); } else log.info("{} uses custom extensions @ {}", context.name(), extensions_file_path); ApplicationConfigurationBinder binder = new ApplicationConfigurationBinder(); return binder.bindExtensions(config); } catch (RuntimeException e) { throw new RuntimeException( "cannot install extensions for application @ " + context.name() + " (see cause) ", e); } } @Override public SmartGearsConfiguration smartgearsConfiguration() { try { InputStream config = getClass().getResourceAsStream(library_configuration_file_path); if (config == null) throw new IllegalStateException("invalid distribution: cannot find " + library_configuration_file_path); SmartGearsConfigurationBinder binder = new SmartGearsConfigurationBinder(); SmartGearsConfiguration configuration = binder.bind(config); configuration.validate(); return configuration; } catch (RuntimeException e) { throw new RuntimeException("cannot read library configuration (see cause) ", e); } } // helpers private ApplicationConfiguration configurationFor(ServletContext application) { try { InputStream config = application.getResourceAsStream(configuration_file_path); if (config == null) return null; ApplicationConfigurationBinder binder = new ApplicationConfigurationBinder(); return binder.bind(config); } catch (RuntimeException e) { throw new RuntimeException("invalid configuration (see cause)", e); } } private ContainerConfiguration containerConfiguration() { if (configFile == null) { String home = Utils.home(); if (home == null) throw new IllegalStateException("invalid node configuration: the environment variable " + ghn_home_env + " or the system property " + ghn_home_property + " must be defined"); File homeDir = new File(home); if (!(homeDir.exists() && homeDir.isDirectory() && homeDir.canRead() && homeDir.canWrite())) throw new IllegalStateException("invalid node configuration: home " + home + " does not exist or is not a directory or cannot be accessed in read/write mode"); configFile = new File(homeDir, container_configuraton_file_path); log.trace("reading container configuration @ {} ", configFile.getAbsolutePath()); } if (!(configFile.exists() && configFile.canRead())) throw new IllegalStateException("invalid node configuration: file " + configFile.getAbsolutePath() + " does not exist or cannot be accessed"); ContainerConfiguration configuration; try (InputStream stream = new FileInputStream(configFile)) { configuration = new ContainerConfigurationBinder().load(stream); } catch (Exception e) { throw new IllegalStateException( "invalid node configuration: file " + configFile.getAbsolutePath() + " is invalid", e); } return configuration; } @Override public synchronized List publishers() { if (this.publishers == null) { Set> annotatedPublishers; try (ScanResult result = new ClassGraph().enableClassInfo().enableAnnotationInfo() .addClassLoader(Thread.currentThread().getContextClassLoader()).scan()) { ClassInfoList classInfos = result.getClassesWithAnnotation(SmartgearsProfilePublisher.class.getName()); annotatedPublishers = classInfos.stream().map(ClassInfo::loadClass) .filter(c -> Publisher.class.isAssignableFrom(c)).collect(Collectors.toSet()); } /* * Collection urls = * ClasspathHelper.forClassLoader(Thread.currentThread().getContextClassLoader() * ); urls.removeIf(url -> url.toString().endsWith(".so") || * url.toString().endsWith(".zip") ); * * * ConfigurationBuilder reflectionConf = new * ConfigurationBuilder().addUrls(urls).setScanners(new * TypeAnnotationsScanner(), new SubTypesScanner()); * * Reflections reflection = new Reflections(reflectionConf); * * = reflection.getTypesAnnotatedWith(SmartgearsProfilePublisher.class); */ List foundPublishers = new ArrayList(); for (Class annotatedPublisher : annotatedPublishers) { try { foundPublishers.add((Publisher) annotatedPublisher.getDeclaredConstructor().newInstance()); log.info("added class {} to publishers", annotatedPublisher); } catch (Throwable e) { log.error("publisher class {} cannot be instantiated", annotatedPublisher.getCanonicalName(), e); } } this.publishers = foundPublishers; if (foundPublishers.isEmpty()) log.warn("no publishers found in classloader"); } return this.publishers; } /* * @Override public AuthorizationProvider authorizationProvider() { return * containerContext.authorizationProvider(); } */ }