resource-registry-handlers/src/main/java/org/gcube/smartgears/handler/resourceregistry/EServiceHandler.java

280 lines
10 KiB
Java

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.
* <p>
* The manager:
* <ul>
* <li>creates the {@link EService} {@link Resource} and the facets it
* {@link ConsistsOf} when the application starts for the first time;</li>
* <li>update the {@link StateFacet} when the application becomes active,
* and at any lifecycle change thereafter;</li>
* </ul>
* </p>
*
* @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<UUID> startContexts, Set<UUID> resourceContexts) {
Set<UUID> 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<String> startTokens = applicationContext.configuration().startTokens();
Set<UUID> startContextsUUID = new HashSet<>();
for (String token : startTokens) {
UUID contextUUID = ContextUtility.getContextUUID(token);
if (create) {
ContextUtility.setContextFromToken(token);
eServiceManager = new EServiceManager(applicationContext);
eServiceManager.createEService();
create = false;
} else {
try {
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);
throw e;
}
}
startContextsUUID.add(contextUUID);
}
Set<UUID> 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;
}
}