package org.gcube.smartgears.managers; import static org.gcube.smartgears.Constants.container_profile_file_path; import static org.gcube.smartgears.lifecycle.container.ContainerState.active; import static org.gcube.smartgears.lifecycle.container.ContainerState.down; import static org.gcube.smartgears.lifecycle.container.ContainerState.failed; import static org.gcube.smartgears.lifecycle.container.ContainerState.stopped; import static org.gcube.smartgears.provider.ProviderFactory.provider; import java.io.File; import java.io.FileOutputStream; import java.io.ObjectOutputStream; import java.util.List; import java.util.Set; import org.gcube.common.events.Observes; import org.gcube.common.events.Observes.Kind; import org.gcube.smartgears.configuration.Mode; import org.gcube.smartgears.context.application.ApplicationContext; import org.gcube.smartgears.context.container.ContainerContext; import org.gcube.smartgears.handlers.ProfileEvents; import org.gcube.smartgears.handlers.container.ContainerHandler; import org.gcube.smartgears.handlers.container.ContainerLifecycleEvent; import org.gcube.smartgears.handlers.container.ContainerPipeline; import org.gcube.smartgears.lifecycle.application.ApplicationLifecycle; import org.gcube.smartgears.lifecycle.container.ContainerState; import org.gcube.smartgears.utils.Utils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Coordinates management of the container as a gCube resource. * * @author Fabio Simeoni * */ public class ContainerManager { private static Logger log = LoggerFactory.getLogger(ContainerManager.class); public static ContainerManager instance = new ContainerManager(); private ContainerContext context; private ContainerPipeline pipeline; private ContainerManager() {} /** * Starts container management. */ public ContainerContext start(ContainerContext context) { this.context = context; try { if (context.configuration().mode()!=Mode.offline) validateContainer(context); saveContainerState(); List handlers = provider().containerHandlers(); log.trace("managing container lifecycle with {}", handlers); startHandlers(handlers); context.lifecycle().moveTo(active); return context; } catch(RuntimeException e) { log.error("cannot manage container (see cause)",e); if (context!=null) context.lifecycle().moveTo(failed); throw e; } } private void saveContainerState() { File file = context.persistenceWriter().file(container_profile_file_path); try(ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file))){ oos.writeObject(context.id()); }catch (Exception e) { log.error("error serializing cointainer state"); throw new RuntimeException(e); } } private void validateContainer(ContainerContext context) { //List tokensToRemove = new ArrayList(); context.configuration().validate(); Set foundContexts; try { foundContexts = context.authorizationProvider().getContexts(); } catch (Exception e) { log.error("error authorizing container",e); throw new RuntimeException("error authorizing container, moving the container to failed",e); } if (foundContexts.isEmpty()){ log.error("no valid contexts found, moving the container to failed"); throw new RuntimeException("no valid contexts found, moving the container to failed"); } } public void manage(ApplicationContext app) { app.events().subscribe(this); } @Observes(value={ApplicationLifecycle.failure,ApplicationLifecycle.stop},kind=Kind.critical) void monitorApplication(ApplicationLifecycle lifecycle) { context.lifecycle().tryMoveTo(ContainerState.partActive); } @Observes(value=ContextEvents.ADD_CONTEXT_TO_CONTAINER,kind=Kind.critical) void addContext(String scope) { log.info("adding context {} event send", context); context.events().fire(scope, ProfileEvents.addToContext); } @Observes(value=ContextEvents.REMOVE_CONTEXT_FROM_CONTAINER,kind=Kind.critical) void removeContext(String scope) { log.info("removing context {} event send", context); context.events().fire(scope, ProfileEvents.removeFromContext); } /** * Stops container management on remote request. * */ public void stop() { stop(false); } /** * Stops container management on remote request or container shutdown. * */ public void stop(boolean shutdown) { //two cases: stop-on-shutdown and stop-on-request, some listeners will be selective about this, //shutdown is triggered by probe app, which is notified among other apps //if other app have been already notified, the container may already be part-active. //apps still to notify will listen only on stop, hence won't react to this but will go down when their turn arrives. if (context == null) return; log.info("stopping container management"); try { context.lifecycle().tryMoveTo(shutdown?down:stopped); stopHandlers(); //no further reactions log.info("stopping container events"); context.events().stop(); Utils.scheduledServicePool.shutdownNow(); } catch (RuntimeException e) { log.warn("cannot stop container management (see cause)", e); } } //helpers private void startHandlers(List handlers) { try { pipeline = new ContainerPipeline(handlers); pipeline.forward(new ContainerLifecycleEvent.Start(context)); } catch (RuntimeException e) { context.lifecycle().tryMoveTo(failed); throw e; } } private void stopHandlers() { if (pipeline == null) return; // copy pipeline, flip it, and ContainerPipeline returnPipeline = pipeline.reverse(); // start lifetime pipeline in inverse order with stop event returnPipeline.forward(new ContainerLifecycleEvent.Stop(context)); } /* private void loadKeyForToken(List tokens) { String initialToken = SecurityTokenProvider.instance.get(); //TODO: change this String filePath = "/tmp/keys"; File PathDirs = new File(filePath+"/"); PathDirs.mkdirs(); try{ for (String token : tokens) { try{ SecurityTokenProvider.instance.set(token); File key = authProvider.getSymmKey(filePath); log.trace("loading key {} file name ",key.getAbsolutePath()); log.trace("loaded key {} file name ",key.getAbsolutePath()); }catch(Exception e){ log.warn("error loading key for token {}", token, e); } } loadFileIntoClasspath(PathDirs); }finally{ SecurityTokenProvider.instance.set(initialToken); } } private void loadFileIntoClasspath(File file){ try { URL url = file.toURI().toURL(); ClassLoader currentClassloader = Thread.currentThread().getContextClassLoader().getParent()==null? Thread.currentThread().getContextClassLoader() : Thread.currentThread().getContextClassLoader().getParent(); URLClassLoader classLoader = (URLClassLoader)currentClassloader; Method method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class); method.setAccessible(true); method.invoke(classLoader, url); } catch (Exception ex) { log.error("error loading file into classpath",ex); } } */ }