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.net.URL; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Set; import java.util.UUID; import javax.servlet.ServletContext; import org.gcube.common.events.Hub; import org.gcube.common.events.impl.DefaultHub; 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.utils.Utils; import org.reflections.Reflections; import org.reflections.scanners.SubTypesScanner; import org.reflections.scanners.TypeAnnotationsScanner; import org.reflections.util.ClasspathHelper; import org.reflections.util.ConfigurationBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * 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); } containerContext = new DefaultContainerContext(id, configuration, hub, lifecycle, 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) { //retrieve from root class loader 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); Set> annotatedPublishers = reflection.getTypesAnnotatedWith(SmartgearsProfilePublisher.class); List foundPublishers = new ArrayList(); for (Class annotatedPublisher: annotatedPublishers) { if (Publisher.class.isAssignableFrom(annotatedPublisher)) try { foundPublishers.add((Publisher)annotatedPublisher.newInstance()); log.info("added class {} to publishers",annotatedPublisher); } catch (Exception e) { log.error("publisher class {} cannot be instantiated", annotatedPublisher.getCanonicalName(),e); } else log.warn("publisher class {} discarded, it doesn't implements Publisher class", annotatedPublisher.getCanonicalName()); } this.publishers = foundPublishers; if (foundPublishers.isEmpty()) log.warn("no publishers found in classloader"); } return this.publishers; } @Override public AuthorizationProvider authorizationProvider() { return containerContext.configuration().authorizationProvider(); } }