common-smartgears/src/main/java/org/gcube/smartgears/provider/DefaultProvider.java

409 lines
13 KiB
Java

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<Publisher> 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<ContainerHandler> 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<ContainerHandler> 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<Publisher> publishers() {
if (this.publishers == null) {
Set<Class<?>> 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<URL> 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<Publisher> foundPublishers = new ArrayList<Publisher>();
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(); }
*/
}