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 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.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.handlers.application.ApplicationLifecycleHandler; 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: *

*

* * @author Luca Frosini (ISTI-CNR) */ @XmlRootElement(name = Constants.RESOURCE_MANAGEMENT) public class EServiceHandler extends ApplicationLifecycleHandler { private static final Logger logger = LoggerFactory.getLogger(EServiceHandler.class); private ApplicationContext applicationContext; private ScheduledFuture periodicUpdates; protected EServiceManager eServiceManager; public EServiceHandler() { super(); } @Override 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(); Set startContextsUUID = new HashSet<>(); ContextCache contextCache = ContextCache.getInstance(); for (String token : startTokens) { if (create) { ContextUtility.setContextFromToken(token); eServiceManager = new EServiceManager(applicationContext); eServiceManager.createEService(); // applicationContext.properties() // .add(new Property(Constants.ESERVICE_MANAGER_PROPERTY, eServiceManager)); create = false; } else { try { UUID contextUUID = ContextUtility.getContextUUID(token); eServiceManager.addToContext(contextUUID); } catch (Exception e) { UUID uuid = UUID.fromString(applicationContext.id()); logger.error("Unable to add {} with UUID {} to current context ({})", EService.NAME, uuid, ContextUtility.getContextName(token), e); } } String contextFullName = ContextUtility.getContextName(token); UUID contextUUID = contextCache.getUUIDByFullName(contextFullName); startContextsUUID.add(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(); if (previousToken == null) { ContextUtility.setContextFromToken(applicationContext.configuration().startTokens().iterator().next()); } try { Thread.currentThread().setContextClassLoader(EServiceHandler.class.getClassLoader()); eServiceManager.updateServiceStateFacet(); } 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(); if (previousToken == null) { ContextUtility .setContextFromToken(applicationContext.configuration().startTokens().iterator().next()); } try { Thread.currentThread().setContextClassLoader(EServiceHandler.class.getClassLoader()); ContextUtility.setContextFromToken(token); eServiceManager.addToCurrentContext(); } 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(); if (previousToken == null) { ContextUtility .setContextFromToken(applicationContext.configuration().startTokens().iterator().next()); } try { Thread.currentThread().setContextClassLoader(EServiceHandler.class.getClassLoader()); eServiceManager.removeFromContext(); } 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; } if (lc.state() == ApplicationState.active) { logger.info("Scheduling periodic updates of {} from application {}", EService.NAME, applicationContext.name()); } else { logger.info("Resuming periodic updates of {} for application {}", EService.NAME, applicationContext.name()); } final Runnable updateTask = new Runnable() { public void run() { String previousToken = SecurityTokenProvider.instance.get(); if (previousToken == null) { ContextUtility.setContextFromToken( applicationContext.configuration().startTokens().iterator().next()); } try { eServiceManager.updateServiceStateFacet(); } catch (Exception e) { logger.error("Cannot complete periodic update of {}", EService.NAME, e); } finally { ContextUtility.setContextFromToken(previousToken); } } }; periodicUpdates = Utils.scheduledServicePool.scheduleAtFixedRate(updateTask, Constants.application_republish_frequency_in_minutes, Constants.application_republish_frequency_in_minutes, TimeUnit.MINUTES); } @Observes(value = { stop, failure }, kind = resilient) synchronized void cancelPeriodicUpdates(ContainerLifecycle ignore) { if (periodicUpdates != null) { logger.trace("Stopping periodic updates of {} for application {} ", EService.NAME, applicationContext.name()); try { periodicUpdates.cancel(true); periodicUpdates = null; } catch (Exception e) { logger.warn("Could not stop periodic updates of {} for application {}", EService.NAME, applicationContext.name(), e); } } } }); } @Override public String toString() { return Constants.RESOURCE_MANAGEMENT; } }